4.2 栈的定义
- 栈
栈是限定仅在表尾进行插入和删除操作的线性表。
我们把允许插入和删除的一端称为 栈顶,另一端称为 栈底,不含任何数据元素的栈称为 空栈。栈又称为后进先出的 线性表,简称 LIFO 结构。
栈的特殊之处在于它限制了这个线性表的插入和删除位置,它始终只在栈顶进行
。
-
栈的插入操作,叫作
进栈
,也称压栈
、入栈
。 - 栈的删除操作,叫作
出栈
,也有的叫作弹栈
。
4.3 栈的抽象数据类型
4.4 栈的顺序存储结构及实现
4.6 栈的链式存储结构及实现
4.7 栈的应用
栈的引入简化了程序设计的问题,划分了不同关注层次,使得思考范围缩小,更加聚焦于我们要解决的问题核心。反之,像数组等,因为要分散精力去考虑数组的下标增减等细节问题,反而掩盖了问题的本质。
所以现在很多高级语言,比如 Java、C#等都有对栈结构的封装,你可以不用关注它的实现细节,就可以直接使用 Stack 的 push 和 pop 方法,非常方便。
栈的应用——递归
在高级语言中,调用自己和其它函数没有本质的不同。我们把一个直接用自己或通过一系列的调用语句间接地调用自己的函数,称作递归函数。
每个递归函数必须至少有一个条件,满足时递归不再执行,即不再引用自身而是返回值退出。
递归和迭代的区别是:迭代使用的是循环结构
,递归使用的是选择结构
。 递归能使程序的结构更清晰、更简洁、更容易让人理解,从而减少读懂代码的时间
。但是大量的递归调用会建立函数的副本,会耗费大量的时间和内存
。迭代则不需要反复调用函数和占用额外的内存。因此我们应该视不同情况选择不同的代码实现方式。
在前行阶段,对于每一层递归,函数的局部变量、参数值以及返回地址都被压入栈中。在退回阶段,位于栈顶的局部变量、参数值和返回地址被弹出,用于返回调用层次中执行代码的其余部分,也就是恢复了调用的状态。
4.10 队列定义
- 队列
队列是只允许在一端进行插入操作、而在另一端进行删除操作的线性表。
队列是一种先进先出的线性表,简称 FIFO。允许插入的一端称为队尾,允许删除的一端称为队头。假设队列是 q = (a1, a2, ……, an),那么 a1 就是队头元素,而 an 就是队尾元素。我们在删除时,总是从 a1 开始;插入时,列在最后。
4.11 队列的抽象数据类型
4.12 循环队列
解决假溢出的办法就是后面满了,就再从头开始,也就是头尾相接的循环。我们把队列的这种头尾相接的顺序存储结构称为循环队列。
4.12.1 队列顺序存储的不足
4.13 队列的链式存储结构及实现
代码实现
01顺序栈
#include "stdio.h" #include "stdlib.h" #include "io.h" #include "math.h" #include "time.h" #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 20 /* 存储空间初始分配量 */ typedef int Status; typedef int SElemType; /* SElemType类型根据实际情况而定,这里假设为int */ /* 顺序栈结构 */ typedef struct { SElemType data[MAXSIZE]; int top; /* 用于栈顶指针 */ }SqStack; Status visit(SElemType c) { printf("%d ",c); return OK; } /* 构造一个空栈S */ Status InitStack(SqStack *S) { /* S.data=(SElemType *)malloc(MAXSIZE*sizeof(SElemType)); */ S->top=-1; return OK; } /* 把S置为空栈 */ Status ClearStack(SqStack *S) { S->top=-1; return OK; } /* 若栈S为空栈,则返回TRUE,否则返回FALSE */ Status StackEmpty(SqStack S) { if (S.top==-1) return TRUE; else return FALSE; } /* 返回S的元素个数,即栈的长度 */ int StackLength(SqStack S) { return S.top+1; } /* 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR */ Status GetTop(SqStack S,SElemType *e) { if (S.top==-1) return ERROR; else *e=S.data[S.top]; return OK; } /* 插入元素e为新的栈顶元素 */ Status Push(SqStack *S,SElemType e) { if(S->top == MAXSIZE -1) /* 栈满 */ { return ERROR; } S->top++; /* 栈顶指针增加一 */ S->data[S->top]=e; /* 将新插入元素赋值给栈顶空间 */ return OK; } /* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */ Status Pop(SqStack *S,SElemType *e) { if(S->top==-1) return ERROR; *e=S->data[S->top]; /* 将要删除的栈顶元素赋值给e */ S->top--; /* 栈顶指针减一 */ return OK; } /* 从栈底到栈顶依次对栈中每个元素显示 */ Status StackTraverse(SqStack S) { int i; i=0; while(i<=S.top) { visit(S.data[i++]); } printf(" "); return OK; } int main() { int j; SqStack s; int e; if(InitStack(&s)==OK) for(j=1;j<=10;j++) Push(&s,j); printf("栈中元素依次为:"); StackTraverse(s); Pop(&s,&e); printf("弹出的栈顶元素 e=%d ",e); printf("栈空否:%d(1:空 0:否) ",StackEmpty(s)); GetTop(s,&e); printf("栈顶元素 e=%d 栈的长度为%d ",e,StackLength(s)); ClearStack(&s); printf("清空栈后,栈空否:%d(1:空 0:否) ",StackEmpty(s)); return 0; }
02链栈
#include "stdio.h" #include "stdlib.h" #include "io.h" #include "math.h" #include "time.h" #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 20 /* 存储空间初始分配量 */ typedef int Status; typedef int SElemType; /* SElemType类型根据实际情况而定,这里假设为int */ /* 链栈结构 */ typedef struct StackNode { SElemType data; struct StackNode *next; }StackNode,*LinkStackPtr; typedef struct { LinkStackPtr top; int count; }LinkStack; Status visit(SElemType c) { printf("%d ",c); return OK; } /* 构造一个空栈S */ Status InitStack(LinkStack *S) { S->top = (LinkStackPtr)malloc(sizeof(StackNode)); if(!S->top) return ERROR; S->top=NULL; S->count=0; return OK; } /* 把S置为空栈 */ Status ClearStack(LinkStack *S) { LinkStackPtr p,q; p=S->top; while(p) { q=p; p=p->next; free(q); } S->count=0; return OK; } /* 若栈S为空栈,则返回TRUE,否则返回FALSE */ Status StackEmpty(LinkStack S) { if (S.count==0) return TRUE; else return FALSE; } /* 返回S的元素个数,即栈的长度 */ int StackLength(LinkStack S) { return S.count; } /* 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR */ Status GetTop(LinkStack S,SElemType *e) { if (S.top==NULL) return ERROR; else *e=S.top->data; return OK; } /* 插入元素e为新的栈顶元素 */ Status Push(LinkStack *S,SElemType e) { LinkStackPtr s=(LinkStackPtr)malloc(sizeof(StackNode)); s->data=e; s->next=S->top; /* 把当前的栈顶元素赋值给新结点的直接后继,见图中① */ S->top=s; /* 将新的结点s赋值给栈顶指针,见图中② */ S->count++; return OK; } /* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */ Status Pop(LinkStack *S,SElemType *e) { LinkStackPtr p; if(StackEmpty(*S)) return ERROR; *e=S->top->data; p=S->top; /* 将栈顶结点赋值给p,见图中③ */ S->top=S->top->next; /* 使得栈顶指针下移一位,指向后一结点,见图中④ */ free(p); /* 释放结点p */ S->count--; return OK; } Status StackTraverse(LinkStack S) { LinkStackPtr p; p=S.top; while(p) { visit(p->data); p=p->next; } printf(" "); return OK; } int main() { int j; LinkStack s; int e; if(InitStack(&s)==OK) for(j=1;j<=10;j++) Push(&s,j); printf("栈中元素依次为:"); StackTraverse(s); Pop(&s,&e); printf("弹出的栈顶元素 e=%d ",e); printf("栈空否:%d(1:空 0:否) ",StackEmpty(s)); GetTop(s,&e); printf("栈顶元素 e=%d 栈的长度为%d ",e,StackLength(s)); ClearStack(&s); printf("清空栈后,栈空否:%d(1:空 0:否) ",StackEmpty(s)); return 0; }
03顺序队列
#include "stdio.h" #include "stdlib.h" #include "io.h" #include "math.h" #include "time.h" #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 20 /* 存储空间初始分配量 */ typedef int Status; typedef int QElemType; /* QElemType类型根据实际情况而定,这里假设为int */ /* 循环队列的顺序存储结构 */ typedef struct { QElemType data[MAXSIZE]; int front; /* 头指针 */ int rear; /* 尾指针,若队列不空,指向队列尾元素的下一个位置 */ }SqQueue; Status visit(QElemType c) { printf("%d ",c); return OK; } /* 初始化一个空队列Q */ Status InitQueue(SqQueue *Q) { Q->front=0; Q->rear=0; return OK; } /* 将Q清为空队列 */ Status ClearQueue(SqQueue *Q) { Q->front=Q->rear=0; return OK; } /* 若队列Q为空队列,则返回TRUE,否则返回FALSE */ Status QueueEmpty(SqQueue Q) { if(Q.front==Q.rear) /* 队列空的标志 */ return TRUE; else return FALSE; } /* 返回Q的元素个数,也就是队列的当前长度 */ int QueueLength(SqQueue Q) { return (Q.rear-Q.front+MAXSIZE)%MAXSIZE; } /* 若队列不空,则用e返回Q的队头元素,并返回OK,否则返回ERROR */ Status GetHead(SqQueue Q,QElemType *e) { if(Q.front==Q.rear) /* 队列空 */ return ERROR; *e=Q.data[Q.front]; return OK; } /* 若队列未满,则插入元素e为Q新的队尾元素 */ Status EnQueue(SqQueue *Q,QElemType e) { if ((Q->rear+1)%MAXSIZE == Q->front) /* 队列满的判断 */ return ERROR; Q->data[Q->rear]=e; /* 将元素e赋值给队尾 */ Q->rear=(Q->rear+1)%MAXSIZE;/* rear指针向后移一位置, */ /* 若到最后则转到数组头部 */ return OK; } /* 若队列不空,则删除Q中队头元素,用e返回其值 */ Status DeQueue(SqQueue *Q,QElemType *e) { if (Q->front == Q->rear) /* 队列空的判断 */ return ERROR; *e=Q->data[Q->front]; /* 将队头元素赋值给e */ Q->front=(Q->front+1)%MAXSIZE; /* front指针向后移一位置, */ /* 若到最后则转到数组头部 */ return OK; } /* 从队头到队尾依次对队列Q中每个元素输出 */ Status QueueTraverse(SqQueue Q) { int i; i=Q.front; while((i+Q.front)!=Q.rear) { visit(Q.data[i]); i=(i+1)%MAXSIZE; } printf(" "); return OK; } int main() { Status j; int i=0,l; QElemType d; SqQueue Q; InitQueue(&Q); printf("初始化队列后,队列空否?%u(1:空 0:否) ",QueueEmpty(Q)); printf("请输入整型队列元素(不超过%d个),-1为提前结束符: ",MAXSIZE-1); do { /* scanf("%d",&d); */ d=i+100; if(d==-1) break; i++; EnQueue(&Q,d); }while(i<MAXSIZE-1); printf("队列长度为: %d ",QueueLength(Q)); printf("现在队列空否?%u(1:空 0:否) ",QueueEmpty(Q)); printf("连续%d次由队头删除元素,队尾插入元素: ",MAXSIZE); for(l=1;l<=MAXSIZE;l++) { DeQueue(&Q,&d); printf("删除的元素是%d,插入的元素:%d ",d,l+1000); /* scanf("%d",&d); */ d=l+1000; EnQueue(&Q,d); } l=QueueLength(Q); printf("现在队列中的元素为: "); QueueTraverse(Q); printf("共向队尾插入了%d个元素 ",i+MAXSIZE); if(l-2>0) printf("现在由队头删除%d个元素: ",l-2); while(QueueLength(Q)>2) { DeQueue(&Q,&d); printf("删除的元素值为%d ",d); } j=GetHead(Q,&d); if(j) printf("现在队头元素为: %d ",d); ClearQueue(&Q); printf("清空队列后, 队列空否?%u(1:空 0:否) ",QueueEmpty(Q)); return 0; }
04链式队列
#include "stdio.h" #include "stdlib.h" #include "io.h" #include "math.h" #include "time.h" #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 20 /* 存储空间初始分配量 */ typedef int Status; typedef int QElemType; /* QElemType类型根据实际情况而定,这里假设为int */ typedef struct QNode /* 结点结构 */ { QElemType data; struct QNode *next; }QNode,*QueuePtr; typedef struct /* 队列的链表结构 */ { QueuePtr front,rear; /* 队头、队尾指针 */ }LinkQueue; Status visit(QElemType c) { printf("%d ",c); return OK; } /* 构造一个空队列Q */ Status InitQueue(LinkQueue *Q) { Q->front=Q->rear=(QueuePtr)malloc(sizeof(QNode)); if(!Q->front) exit(OVERFLOW); Q->front->next=NULL; return OK; } /* 销毁队列Q */ Status DestroyQueue(LinkQueue *Q) { while(Q->front) { Q->rear=Q->front->next; free(Q->front); Q->front=Q->rear; } return OK; } /* 将Q清为空队列 */ Status ClearQueue(LinkQueue *Q) { QueuePtr p,q; Q->rear=Q->front; p=Q->front->next; Q->front->next=NULL; while(p) { q=p; p=p->next; free(q); } return OK; } /* 若Q为空队列,则返回TRUE,否则返回FALSE */ Status QueueEmpty(LinkQueue Q) { if(Q.front==Q.rear) return TRUE; else return FALSE; } /* 求队列的长度 */ int QueueLength(LinkQueue Q) { int i=0; QueuePtr p; p=Q.front; while(Q.rear!=p) { i++; p=p->next; } return i; } /* 若队列不空,则用e返回Q的队头元素,并返回OK,否则返回ERROR */ Status GetHead(LinkQueue Q,QElemType *e) { QueuePtr p; if(Q.front==Q.rear) return ERROR; p=Q.front->next; *e=p->data; return OK; } /* 插入元素e为Q的新的队尾元素 */ Status EnQueue(LinkQueue *Q,QElemType e) { QueuePtr s=(QueuePtr)malloc(sizeof(QNode)); if(!s) /* 存储分配失败 */ exit(OVERFLOW); s->data=e; s->next=NULL; Q->rear->next=s; /* 把拥有元素e的新结点s赋值给原队尾结点的后继,见图中① */ Q->rear=s; /* 把当前的s设置为队尾结点,rear指向s,见图中② */ return OK; } /* 若队列不空,删除Q的队头元素,用e返回其值,并返回OK,否则返回ERROR */ Status DeQueue(LinkQueue *Q,QElemType *e) { QueuePtr p; if(Q->front==Q->rear) return ERROR; p=Q->front->next; /* 将欲删除的队头结点暂存给p,见图中① */ *e=p->data; /* 将欲删除的队头结点的值赋值给e */ Q->front->next=p->next;/* 将原队头结点的后继p->next赋值给头结点后继,见图中② */ if(Q->rear==p) /* 若队头就是队尾,则删除后将rear指向头结点,见图中③ */ Q->rear=Q->front; free(p); return OK; } /* 从队头到队尾依次对队列Q中每个元素输出 */ Status QueueTraverse(LinkQueue Q) { QueuePtr p; p=Q.front->next; while(p) { visit(p->data); p=p->next; } printf(" "); return OK; } int main() { int i; QElemType d; LinkQueue q; i=InitQueue(&q); if(i) printf("成功地构造了一个空队列! "); printf("是否空队列?%d(1:空 0:否) ",QueueEmpty(q)); printf("队列的长度为%d ",QueueLength(q)); EnQueue(&q,-5); EnQueue(&q,5); EnQueue(&q,10); printf("插入3个元素(-5,5,10)后,队列的长度为%d ",QueueLength(q)); printf("是否空队列?%d(1:空 0:否) ",QueueEmpty(q)); printf("队列的元素依次为:"); QueueTraverse(q); i=GetHead(q,&d); if(i==OK) printf("队头元素是:%d ",d); DeQueue(&q,&d); printf("删除了队头元素%d ",d); i=GetHead(q,&d); if(i==OK) printf("新的队头元素是:%d ",d); ClearQueue(&q); printf("清空队列后,q.front=%u q.rear=%u q.front->next=%u ",q.front,q.rear,q.front->next); DestroyQueue(&q); printf("销毁队列后,q.front=%u q.rear=%u ",q.front, q.rear); return 0; }