文章目录
一、 顺序栈
顺序栈的基本概念、初始化、入栈、出栈、清空、销毁
顺序栈存储方式:同一般线性表的顺序存储结构完全相同,利用一组地址连续的存储单元依次存方自栈底到栈顶的数据元素,栈底一般在低地址端。
- 附设top指针,指示栈顶元素在顺序栈中的位置。但是为了操作方便,通常top指示真正的栈顶元素之上的下标地址。
- 另设base指针,指示栈底元素在顺序栈中的位置。
- 另外,用stacksize表示栈可使用的最大容量。
判断栈满栈空
空栈:base = =top是栈空的标志
栈满:top-base= =stacksize
栈满时的处理方法
1、报错,返回操作系统
2、分配更大的空间,作为栈的存储空间,将原栈内容移入新栈(缺点就是太费时)
使用数组作为顺序栈存储方式的特点:
简单、方便但易产生溢出(数组大小固定)
- 上溢(overflow):栈已经满,又要压入元素
- 下溢(underflow):栈已经空,还要弹出元素
注:上溢是一种错误,使问题的处理无法进行;而下溢一般认为是一种结束条件,即问题处理结束
先表明一下预定义常量及类型:
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define true 1
#define false 0
typedef int Status;
顺序栈的表示
#define MAXSIZE 100 //顺序栈存储空间初始分配量
typedef struct{
SElemType *data; //栈顶指针
SElemType *top; //栈尾指针
int stacksize; //栈可用最大容量
}SqStack;
顺序栈初始化
Status InitStack(SqStack &s){//构造一个空栈
S.base = new SElemType{MAxSIZE];
//或用c语言语法:S。base=(SElemType*)malloc(MAXSIZE*sizeof(SElemType));
if(!S.base)//S.base是栈中的第一个元素
exit(OVERFLOW);//空间分配失败
S.top=S.base;//栈顶指针等于栈底指针
S.stacksize=MAXSIZE;//stacksize置为最大容量MAXSIZE
顺序栈入栈
Status Push(SqStack &S,int e){//插入e,为新的栈顶元素
if(S.top-S.base==S.stacksize)//栈满
*S.top=e;//取栈顶指针所指空间存e
S.top++;//栈顶指针加一
//上面两句可以合并成一句*S.top++=e;因为算法优先级问题,现存e再指针加一
return OK;
顺序栈出栈
Status Pop(SqStack &S,int e){//删除S的栈顶元素,用e返回其值
if(S.top==S.base) //栈空
return ERROR; //若空则出错(下溢)
--S.top; //栈顶指针减一
e==*S.top; //将栈顶元素赋给e,带*表示指针所指的那块空间
//同样的,上面两句可以合并为e==*--S.top;
return OK;
顺序栈取栈顶元素
int GetTop(SqStack S){//返回S的栈顶元素,不修改栈顶指针
if(S.top!=S.base) //栈不为空
return *(S.top-1); //返回栈顶元素的值,栈顶指针不变,即还在栈顶的上方
【算法补充】
顺序栈判断栈是否为空:
Status StackEmpty(SqStack S)//若栈为空,返回TURE,否则返回FLALSE
if(S.top==S.base)
return TRUE;
else
return FALSE;
}
求顺序栈长度:
int StackLength(SqStack S)
{
return S.top-S.base;
}
清空顺序栈(空间还在,只是删除元素):
Status ClearStack(Sqstack S){
if(S.base) //如果base存在,即栈存在
S.top=S.base;//把base的值赋值给top
return OK;
销毁顺序栈(空间不在):
Status DestroyStack(SqStack &S){
if(S.base){
delete S.base;//
S.stacksize=0; //栈内空间的个数为0
S.base=S.top=NULL;//指针为空
}
return OK;
}
二、链栈
用链式存储结构实现的栈。
链栈的结点结构与单链表的结构相同,在此用SNode表示;
链栈的表示:
- 链栈是运算受限的单链表,只能在链表头部进行操作
typedef struct SNode{
Selem data;
struct SNode *next;//嵌套定义
}SNode,*LInkStack;//栈的结点类型。*Linkstack是指针类型
LinkStack S;
链栈中指针的方向,一般栈顶元素指向前驱元素,以此类推,目的是操作方便
- 链表的头指针就是栈顶
- 不需要头结点
- 基本不存在栈满的情况,只要内存有空间
- 空栈相当于头指针指向空,S=NULL;
- 插入和删除仅在栈顶处执行
链栈的初始化
void InitStack(LinkStack &S){//构造一个空栈S,栈顶指针置空
S=NULL;
return OK;
}
【算法补充】
判断链栈是否为空
Status StackEmpty(LinkStack S){
if(S==NULL)//头指针为空就是空栈
return TRUE;
else
return FALSE;
链栈的入栈
插的新元素放入栈顶
Status Push(LinkStack &S,int e){
SNode *p=new SNode //生成新结点p
p->data=e; //将新结点数据域值为e
p->next=S; //将新结点插入栈顶,p的指针域指向下面结点地址也就是原来的头指针S,
S=p; //修改栈顶指针,也就是p变为S,S指向了新结点
return OK;
链栈的出栈
Status Pop(LinkStack &S,int &e){//删除栈顶S的栈顶元素,用e返回其值
if(S==NULL) //栈空,无元素可以删除
return ERROR;
e=S->data; //将栈顶元素赋给e,以便取出
SNode *p=S; //p指向S即用p临时保存栈顶元素空间,以备释放
S=S->next; //修改栈顶指针
delete p; //释放原栈顶元素空间
return OK;
}
取栈顶元素
int GetTop(LinkStack S){
if(S!=NULL)
return S->data;
}
三、顺序栈和链栈的对比
相同:时间复杂度一样,均为O(1)
不同:
- 顺序栈需事先确定一个固定的长度,可能会存在内存空间浪费的问题,优势是存取是定位很方便
- 链栈则要求每个元素都有指针域,这同时也增加了一些内存开销,但对于栈的长度无限制
那么我们如何选择呢?
如果栈的使用过程中元素变化不可预料,有时很小,有时非常大,那么最好是用链表,反之,如果变化在可控范围内,建议使用顺序栈。