栈——栈和队列考研笔记
代码基本来源:2020王道数据结构考研复习指导 (侵删)
文章目录
1. 栈的定义
栈是只允许在一端进行插入或删除操作的线性表
-
特点:后进先出 Last In First Out(LIFO)
-
逻辑结构:与普通线性表相同(元素之间一一对应)
-
数据的运算:与普通线性表的插入、删除操作有区别
-
重要术语:栈顶、栈底、空栈
- 栈顶:允许插入和删除的一端
- 栈底:不允许插入和删除的一端
- 空栈:栈中没有任何元素
2. 栈的实现方法
2.1 顺序栈 SqStack(Sequence Stack)
- 物理结构(存储方式):用顺序存储的方式实现的栈(静态数组)
- 给各个数据元素分配连续的存储空间,大小为
MaxSize*sizeof(ElemType)- 缺点:预先需要分配一大片的连续空间(静态数组)
2.1.1 顺序栈的定义
#define MaxSize 10//定义栈中元素的最大个数
typedef struct{
ElemType data[MaxSize];//静态数组存放栈中元素
int top;//栈顶指针(数组下标)
}SqStack;
2.1.2 顺序栈的初始化
void InitStack(SqStack &S){
S.top = -1;//初始化栈顶指针
}
2.1.3 顺序栈的判空
bool StackEmpty(SqStack S){
if (S.top == -1)//栈空
return true;
else //不空
return false;
}
2.1.4 顺序栈的进栈操作Push
进栈,若栈S未满,则将x加入使之成为新栈顶。
bool Push(SqStack &S,ElemType x){
if (S.top == MaxSize - 1)
return false;//栈满,报错
//栈S未满,可将新元素入栈
S.data[++S.top] = x;//指针先加一,然后新元素入栈
}
2.1.5 顺序栈的出栈操作Pop
出栈,若栈S非空,则弹出栈顶元素,并用x返回(引用)
void Pop(SqStack &S,ElemType &x){
if (S.top == - 1)
return false;//栈空,报错
//栈S未空,可将元素出栈
x = S.data[S.top--];//元素先出栈,然后指针减一
}
2.1.6 顺序栈的读栈顶元素操作(查找操作)
读栈顶元素:若栈S非空,则用x 返回栈顶元素
栈的使用场景中大多只访问栈顶元素
bool GetTop(S, &x){
if (S.top == -1)//栈空,报错
return false;
//栈非空,读栈顶元素
x = S.data[S.top];//x记录栈顶元素
return true;
}
2.1.7 顺序栈的代码注意
- top 指向插入位置 or 指向栈顶元素 (二者各种具体操作的具体实现不同)
- top 指向栈顶元素:初始化 top = -1
- top 指向插入位置:初始化 top = 1
2.2 共享栈 ShStack(Shareing Stack)
- 两个栈共享同一片空间
- 优点:相比于顺序栈(一个栈一片空间),可以节省内存空间
- 缺点:仍需要声明栈的时候分配一片连续的存储空间
2.2.1 共享栈的定义
- top0和top1代表两个栈的栈顶指针
- top0:由小变大(数组下标随着元素入栈逐渐增加)
- top1:由大变小(数组下标随着元素入栈逐渐减少)
#define MaxSize 10//定义栈中元素的最大个数
typedef struct{
ElemType data[MaxSize];//静态数组存放栈中元素
int top0;//0号栈栈顶指针
int top1;//1号栈栈顶指针
}ShStack;
2.2.2 共享栈的初始化
- top0和top1代表两个栈的栈顶指针
- top0 = -1:0号栈中没有元素
- top1 = MaxSize:1号栈中没有元素
void InitStack(ShStack &s){
S.top0 = -1;//初始化栈顶指针
S.top1 = MaxSize;
}
2.2.2 共享栈的栈满条件
- top0 + 1 == top1:两个栈顶指针紧挨着(碰头)
2.3 链栈 LiStack(Linklist Stack)
- 用链式存储方式实现的栈:存储结构和单链表相同
- 对单链表的插入和删除操作限制即可
- 进栈/出栈都只能在栈顶一端进行(链头作为栈顶)
- 以下实现方式的对象:不带头结点的链栈
2.3.1 链栈的定义
typedef struct LinkNode{
ElemType data;//数据域
struct LinkNode *next;//指针域
} *LiStack;//栈类型定义
2.3.2 链栈的初始化
bool InitLiStack(LiStack &S){
S = NULL;//初始化栈顶指针
return true;
}
2.3.3 链栈的判空
bool LiStackEmpty(LiStack S){
if (S == NULL)//栈顶指针为空——>空栈
return true;
else
return false;
}
2.3.4 链栈的进栈操作(链表的后插操作)
bool Push(LiStack &S, ElemType x){
LinkNode * t = (*LinkNode)malloc(sizeof(LinkNode));//动态分配新的结点
if (t == NULL)
return false;//内存不足,分配失败
t->data = x;//填充结点
if (S == NULL){
//栈空
S = t;//栈顶指针指向t
return true;
}
//栈不空
t->next = S;//将原栈顶结点连到新结点之后
S = t;//栈顶指针指向最新入栈的结点
return true;
}
2.3.5 链栈的出栈操作
bool Pop(LiStack &S, ElemType &x){
if (S == NULL)//栈空,报错
return false;
LinkNode * t = S;//记录当前的栈顶指针
x = S->data;//栈顶结点出栈
S = S->next;//栈顶指针指向下一个结点
free(t);//释放原栈顶结点的内存
return true;
}
2.3.6 链栈栈顶元素的获取
bool GetTop(S, &x){
if (S == NULL)//栈空,报错
return false;
//栈非空,读栈顶元素
x = S->data;//x记录栈顶元素
return true;
}
2.3.7 链栈的代码实现注意事项
- 不带头结点比带头结点的实现方式更方便(区别于普通链表)
- 链栈的栈顶指针始终指向最新入栈的结点