基本概念
Stack是只允许在一端进行插入和删除的线性表。
常见术语
栈顶(允许进行插入和删除的一端) 栈底 空栈
特点
LIFO 卡特兰数
栈的基本操作(创/销/增/删/查/判空,判满)
InitStack(&S) DestoryStack(&S) Push(&S,x) Pop(&S,&x) GetTop(S,&x) StackEmpty(S)
栈的顺序存储结构
利用一组地址连续空间,同时设一个top指针指示当前栈顶元素的位置
空间不足会导致上溢
//顺序栈 #include<stdio.h> #include<stdlib.h> #define MaxSize 50 /* InitStack(&S) DestoryStack(&S) Push(&S,x) Pop(&S,&x) GetTop(S,&x) StackEmpty(S) */ typedef struct{ int data[MaxSize]; int top; }SqStack; //顺序栈 void InitStack(SqStack &S){ S.top=-1; //初始化栈顶指针,其实也可以初始化为0 } //bool DestoryStack(SqStack &S){ // //} bool Push(SqStack &S,int x){ //进栈 if(S.top==MaxSize-1) return false; //进栈时首先判满 S.data[++S.top]=x; return true; } bool Pop(SqStack &S,int &x){ //出栈 if(S.top==-1) return false; //出栈时首先判断是否是空栈 x=S.data[S.top--]; return true; } bool GetTop(SqStack S,int &x){ //读取栈顶元素 if(S.top==-1) return false; //首先判断是否是空栈 x=S.data[S.top]; return true; } bool StackEmpty(SqStack S){ //判空 if(S.top==-1) return true; else return false; } int main(){ SqStack S; int x,y=0; scanf("%d",&x); InitStack(S); Push(S,x); GetTop(S,y); printf("%d",y); }
书上将顺序表唯一的指针初始化为-1,也可以初始化为0。
共享栈
两个指针top0和top1,top为-1,top1为MaxSize;当两个指针之间相差一,则满(top1-top0=1),只是为了有效地利用存储空间,时间复杂度还是为O(1),对存储效率没有什么影响。
//共享栈 #include<stdio.h> #include<stdlib.h> #define MaxSize 50 /* InitStack(&S) DestoryStack(&S) Push(&S,x) Pop(&S,&x) GetTop(S,&x) StackEmpty(S) */ typedef struct{ int data[MaxSize]; int top0; int top1; }ShStack; //共享栈 void InitStack(SqStack &S){ S.top0=-1; //初始化指针,其实也可以初始化为0 S.top1=MaSize; } /* 以下出栈,进栈,判空,读栈顶元素函数方法都相类似 */
栈的链式存储结构
链栈优点:便于多个栈共享存储空间和提高其效率,通常采用单链表实现。并规定所有的操作都是在单链表的表头进行的,书上规定,链栈没有头节点,Lhead指向栈顶元素。
【注意】带头节点和不带头节点的链栈,具体实现会有所不同
【不懂】上图中7行,声明一个指针,不是应该写为struct LinkNode *next吗?难道这个struct可以省略。// 链栈 #include<stdio.h> #include<stdlib.h> //定义结构体 typedef struct LinkNode{ int data; //Node *next; 这样写报错 LinkNode *next; //前面写不写struct都不会报错 }Node, *LiStack; //初始化栈 void InitStack(LiStack &S){ S=NULL; } //判断空栈 bool StackEmpty(LiStack S){ if(S==NULL) return true; else return false; } //进栈 bool Push(LiStack &S,int x){ //不需要判空判满 Node *p=(Node *)malloc(sizeof(Node)); //因为p为指针,故不能p.data=x;p.next=S这样 p->data=x; p->next=S; //相当于头插法 S=p; return true; } //出栈 bool Pop(LiStack &S,int &x){ //出栈需要判断栈是否为空 if(S==NULL) return false; /* x=S->data; S=S->next; return true; 这样写出错误,因为没有释放这个节点*/ Node *p=S; //这样写还是通过malloc函数写? x=p->data; S=p->next; free(p); return true; } //取栈顶元素 bool GetTop(LiStack S,int &x){ //判空 if(S==NULL) return false; x=S->data; return true; } //销栈 void DestoryStack(LiStack &S){ int e; while(S!=NULL) Pop(S,e); free(S); } void Proint(LiStack S){ if(StackEmpty(S)){ printf("空栈\n"); return;//也可以不用写这句 } Node *p=S; while(p!=NULL){ printf("%d ",p->data); p=p->next; } printf("\n"); } int main(){ LiStack S; printf("***************************************\n"); printf("******** 1.初始化栈 2.空栈判断 ********\n"); printf("******** 3. 进 栈 4. 出 栈 ********\n"); printf("******** 5.读取栈顶 6. 销 栈 ********\n"); printf("******** 7. 遍 历 0. 退 出 ********\n"); printf("***************************************\n"); bool flag= true; int x; int e; while(flag) { printf("请输入命令:"); scanf("%d", &x); switch (x) { case 1: InitStack(S); printf("\t空栈初始化完成\n"); break; case 2: if (StackEmpty(S)) printf("\t该栈是空栈\n"); else printf("\t该栈不是空栈\n"); break; case 3: printf("\t请输入要进栈的元素:"); int a; scanf("%d", &a); if (Push(S, a))printf("\t元素进栈成功\n"); else printf("\t元素进栈失败\n"); break; case 4: if (Pop(S, e)) printf("\t元素出栈成功,出栈的元素为:%d\n", e); else printf("\t元素进栈失败\n"); break; case 5: if (GetTop(S, e)) printf("\t栈顶元素为:%d\n", e); else printf("\t该栈为空栈\n"); break; case 6: DestoryStack(S); printf("\t销毁成功\n"); break; case 7: printf("\t当前栈中元素:"); Proint(S); break; case 0: printf("退出成功\n"); flag = false; break; default: printf("命令错误,请重新输入!!!"); break; } } }