一、释义
1.栈的定义
栈是只允许在一端进行插入或者删除操作的线性表。栈是一种线性表(线性表又包括顺序表和链表),栈也包括链栈和顺序栈。 如果所示:
2.基本术语
栈顶:线性表允许进行插入或者删除的那一端
栈底:不允许插入或者删除的一端
空栈:不含任何元素的栈
栈顶元素:栈中最高的元素
栈顶指针:指向栈顶元素或者栈顶元素的下一个位置(根据具体问题而定)
如上图所示:栈顶元素为a5,栈底元素为a1。若只进行插入或者删除操作即能确定入栈次序或者出栈次序,则上图得入栈次序就是a1,a2,a3,a4,a5。出栈次序就是a5,a4,a3,a2,a1。所以可以得出:栈的特性就是后进先出。并且栈也是一种操作受限的线性表。
卡特兰数:n个不同元素进栈,出栈元素的不同排列个数为(因为元素不是一次性全部进栈或者一次性全部出栈的)
二、顺序栈的基本操作
1.顺序栈定义
使用顺序存储的栈称为顺序栈,和顺序表一样顺序栈也是利用一组地址连续的存储空间存放从栈底到栈顶数据元素,同时只设置一个栈顶指针即可(不需要设置栈底指针,因为不允许对栈底进行操作)指向当前栈顶元素。
#include<stdio.h>
#define MaxSize 10 //这个栈能存储的最大元素
typedef struct{
int data[MaxSize];
int top; //栈顶指针
}SqStack;
2.栈初始化
栈初始化时将栈顶指针设为-1。栈顶元素永远是S.data[S.top]。
//栈初始化
void InitStack(SqStack &s){
s.top=-1; //初始化栈顶指针为-1 注:要注意栈顶指针指向栈顶元素还是栈顶元素的上一个空间
}
如图所示为刚初始化完后的栈(此时栈空):
3.栈判空
直接判断栈顶指针是不是-1即可
//判空操作
bool StackEmpty(SqStack s){
if(s.top==-1){
return true;
}
return false;
}
4.元素进栈
若栈满则直接return;栈不满时先栈顶指针+1再进行赋值操作,此算法定义的是栈顶指针指向栈顶元素 若先赋值在自增则就将原来元素覆盖了。但是若栈顶指针指向栈顶元素的下一个位置,则需要先赋值再自增。具体问题具体分析
//元素入栈
bool Push(SqStack &s,int e){
if(s.top==MaxSize-1){ //栈满则无法进
return false;
}
s.top++; //栈顶指针加一
s.data[s.top]=e; //也可简化为 s.data[++s.top]=e 先自增再赋值
return true;
}
若图所示为进了一个元素的栈:
5.元素出栈
栈空时,栈顶指针指向-1,先判空,若空则return;若非空则先将元素出栈(赋值给e,让e返回去),指针再减一。[s.top--]为先赋值再自减语法。
//元素出栈 返回出栈元素
bool Pop(SqStack &s,int &e){
if(s.top==-1){ //栈顶指针指向-1说明没有元素 无法出栈
return false;
}
e=s.data[s.top--]; //先出栈 指针再-1
return true;
}
如图所示为元素a3出栈图例。但是注意 我们不需要将原来的元素置空直接移动栈顶指针即可,因为如果此栈以后还要进行入栈 原来未被置空的元素值就被覆盖了
6.读取栈顶元素
同样先进行判空,若栈不空则直接返回。注意:读取栈顶元素仅仅只是读取了这个值,原元素并未出栈(因为栈顶指针没移动)
//读取栈顶元素
bool GetTop(SqStack s,int &e){ //注:不需要对栈进行更改 故不用引用符号 !
if(s.top==-1){
return false;
}
e=s.data[s.top];
return true;
}
三、顺序栈完整代码
#include<stdio.h>
#define MaxSize 10 //这个栈能存储的最大元素
typedef struct{
int data[MaxSize];
int top; //栈顶指针
}SqStack;
//栈初始化
void InitStack(SqStack &s){
s.top=-1; //初始化栈顶指针为-1 注:要注意栈顶指针指向栈顶元素还是栈顶元素的上一个空间
}
//判空操作
bool StackEmpty(SqStack s){
if(s.top==-1){
return true;
}
return false;
}
//元素入栈
bool Push(SqStack &s,int e){
if(s.top==MaxSize-1){
return false;
}
s.top++;
s.data[s.top]=e; //也可简化为 s.data[++s.top]=e 先自增再赋值
return true;
}
//元素出栈 返回出栈元素
bool Pop(SqStack &s,int &e){
if(s.top==-1){
return false;
}
e=s.data[s.top--];
return true;
}
//读取栈顶元素
bool GetTop(SqStack s,int &e){ //注:不需要对栈进行更改 故不用引用符号 !
if(s.top==-1){
return false;
}
e=s.data[s.top];
return true;
}
//test
int main(){
SqStack s;
InitStack(s);
Push(s,2);
Push(s,4);
Push(s,5);
Push(s,6);
Push(s,7);
int e;
Pop(s,e);
printf("%d\n",e);
GetTop(s,e);
printf("%d\n",e);
}
四、共享栈
1.共享栈释义
利用栈底位置的相对不变性,可以让两个栈共享一片连续空间(一维数组),将两个栈的栈底分别设置在共享空间的两端,两个栈顶向共享空间的中间延伸。
2.共享栈图例
两个栈的栈顶指针都指向栈顶元素,top0=-1时0号栈为空,top1=MaxSize时1号栈为空不是MaxSize-1。只有当两个栈相邻时(top1-top0=1)判断共享空间满。当0号栈进栈时,top0指针先+1元素再入栈。而对于1号栈则是top指针先减一,元素再入栈(结合图示易于理解)出栈时刚好相反。共享栈的出现是为了更好地利用存储空间。
五、链栈
1.释义
采用链式存储的栈为链栈。链栈是为了多个栈共享存储空间和提升效率(顺序栈具有顺序表的缺点)且不存在栈溢出的缺陷,通常使用单链表实现,根据栈的操作受限性质,所有操作都是在单链表的表头进行的。
2.链栈图例
六、链栈基本操作
1.链栈定义
#include<stdio.h>
#include<stdlib.h>
//链栈的实质就是 单链表使用头插法 进入只能从头进入 出来也只能从头出来
typedef struct LinkNode{
int data;
struct LinkNode *next;
}*LiStack;
2.链栈初始化(带头结点)
//链标初始化(带头结点)
void InitStack(LiStack &S){
S->data=0;
S->next=NULL;
}
3.链栈进栈
//进栈(实质为头插)
bool Push(LiStack &S,int e){
LinkNode *p=(LinkNode*)malloc(sizeof(LinkNode));
p->data=e;
//如果为第一个结点 直接插入即可
if(S->next==NULL){
S->next=p;
return true;
}
p->next=S->next;
S->next=p;
return true;
}
4.链栈出栈
//出栈(也是从头结点下一个开始出)
bool Pop(LiStack &S,int &e){
if(S->next==NULL){
return false;
}
LinkNode *p=(LinkNode*)malloc(sizeof(LinkNode));
p=S->next;
S->next=p->next;
e=p->data;
free(p);
}
七、链栈完整代码
#include<stdio.h>
#include<stdlib.h>
//链栈的实质就是 单链表使用头插法 进入只能从头进入 出来也只能从头出来
typedef struct LinkNode{
int data;
struct LinkNode *next;
}*LiStack;
//链标初始化(带头结点)
void InitStack(LiStack &S){
S->data=0;
S->next=NULL;
}
//进栈(实质为头插)
bool Push(LiStack &S,int e){
LinkNode *p=(LinkNode*)malloc(sizeof(LinkNode));
p->data=e;
//如果为第一个结点 直接插入即可
if(S->next==NULL){
S->next=p;
return true;
}
p->next=S->next;
S->next=p;
return true;
}
//出栈(也是从头结点下一个开始出)
bool Pop(LiStack &S,int &e){
if(S->next==NULL){
return false;
}
LinkNode *p=(LinkNode*)malloc(sizeof(LinkNode));
p=S->next;
S->next=p->next;
e=p->data;
free(p);
}
//test
int main(){
LiStack S;
InitStack(S);
Push(S,5);
Push(S,8);
int e;
Pop(S,e);
printf("%d\n",e);
Pop(S,e);
printf("%d\n",e);
}