一.栈的定义
栈是限定仅在表尾进行插入和删除操作的线性表
把允许插入和删除的一端称为栈顶top
另一端称为栈底bottom
不含有任何数据元素的称为空栈
栈被称为Last In First Out的线性表,简称LIFO结构
二.栈的顺序存储结构及实现
栈的顺序结构存储也叫顺序栈
栈的结构定义:
template <class T>
typedef struct{
T data[maxSize];
int top; //用于栈顶指针
}SeqStack;
栈的进栈操作算法思路:
-
判断是否栈满
-
栈顶指针+1
-
将元素插入到栈中
template <class T>
bool Push(SeqStack *s,T e){
if(top==maxSize-1){
return false;
}
s.top++;
s.data[s.top]=e;
return true;
}
栈的出栈操作算法思路:
-
判断是否栈空
-
将所要删除的元素的数据带出
-
栈顶指针-1
template <class T>
bool Pop(SeqStack *s,T &e){
if(s.top==-1){
return false;
}
e=s.data[s.top];
top--;
return true;
}
两个操作没有任何循环语句,所以时间复杂度都是O(1)
三.栈的链式存储及实现
栈的链式存储简称链栈
链栈的结构体如下:
template <class T>
typedef struct StackNode{
T data; //数据域
struct StackNode *next; //指针域
}*StackNode;
template <class T>
typedef struct{
StackNode top; //定义栈顶TOP指针
int count; //记录链表的元素个数
}LinkStack;
链栈的进栈操作,其算法思路如下:
-
给进栈元素申请一个新的结点
-
将元素的值赋给新结点的数据域
-
类似于单链表的插入操作,将新结点接入到链表当中
-
将count++;
其代码如下:
template <class T>
bool Push(LinkStack* s,T x){
LinkStack* newNode=new LinkStack;
newNode.data=x;
newNode->next=s.top; //把上一个元素赋值给新节点的直接后继,实现连接
s.top=newNode; //将新的结点赋值给栈顶指针
s.count++;
return true;
}
链栈基本不存在栈满的问题,除非是计算机的内存不够,所以我们就不用像顺序栈那样考虑栈满的问题
链栈的出栈操作,其算法思路如下:
-
定义一个新结点p
-
首先判断是否栈空,如果栈空返回错误
-
否则,将删除的结点的数据带出
-
然后类似单链表的删除操作删除结点,释放空间
-
将count--;
-
出栈成功
其代码如下:
template <class T>
bool Pop(LinkStack *s,T &x){
LinkStack *p;
if(s!=NULL){
return false;
}
x=s.data;
p=s.top;
s.top=s.top.next;
delete p;
s.count--;
return true;
}
如果使用过程中元素内容变化特别大,建议使用链栈;
否则,一般是使用顺序栈更好
四.两栈共享空间
一般是用于俩个相同类型的栈 ,完全可以用一个数组来存储两个栈
栈满的条件是top1+1==top2
即两个栈顶指针见面的时候
使用这样的数据结构通常是两个栈具有相反需求关系的时候
这里不做过多讲解
下一篇文章会专门讲到栈的应用