栈和队列是两种特殊的线性结构,从数据结构的角度来看,栈和队列也是线性表。他们的特点在于仅限在表头或者表尾进行插入或者删除的操作。栈和队列都是特殊的线性结构,所以他们的实现方式也包括顺序存储和链式存储两种方式。
栈(stack)
栈是限定仅在表尾进行插入和删除操作的线性表。
因此我们把允许插入和删除的一段叫做栈顶(top),另一端称作栈底(bottom)。没有任何数据元素的栈叫做空栈。
如图所示,栈的插入与删除过程已经很明确了,看的出来栈是一种后进先出(Last In First Out,LIFO)的结构。
- 栈的插入操作,叫做进栈,也称压栈、入栈。
- 栈的删除操作,叫做出栈,也可以称作弹栈。
栈的抽象数据类型
ADT 栈(STACK)
{
DATA
同线性表。元素具有相同的类型,相邻元素具有前驱后继关系。
OPERATION
InitStack(&S) 构造一个空栈S
DsetroyStack(&S) 若栈存在,销毁栈
ClearStack(&S) 将栈清空
StackEmpty(S) 若栈存在元素返回False,否则返回True
GetTop(S) 得到栈顶数据元素
Push(&S,e) 若栈存在,将新元素e插入到栈顶
Pop(&S,&e) 删除栈顶元素,并用e返回其值
StackLength(S) 返回栈的元素个数
StackTraverse(S) 从栈底到栈顶依次对S的每个数据元素进行访问
}
顺序栈的表示和实现
顺序栈的存储结构:
#define MAXSIZE 100 //顺序栈存储空间的初始分配量
typedef struct {
SElemType *base; //栈底指针
SElemType *top; //栈顶指针
int StackSize; //栈可用最大容量
}SqStack;
base为栈底指针,初始化之后,始终指向栈底的位置;若base的值为NULL,则表明栈不存在。top为栈顶指针,其初值指向栈底,入栈时*top++;出栈时*top--。
顺序栈的初始化
【算法步骤】
- 为顺序栈动态分配一个最大容量为MAXSIZE的数组空间,使base指向这段空间的基地址,即栈底
- 栈顶指针top初始化为base,表示栈为空
- stacksize置为栈的最大容量MAXSIZE
//构造一个空栈
Status InitStack(SqStack &S)
{
S.base=new SElemType[MAXSIZE];
if(!S.base)
exit(OVERFLOW);
S.top=S.base;
S.StackSize=MAXSIZE;
return true;
}
顺序栈的入栈
【算法步骤】
- 判断栈是否已满,若满则返回false
- 将新元素压入栈顶,返回true
//在栈顶插入新元素e
Status Push(SqStack &S,SElemType e)
{
if(S.top-S.base==S.StackSize)
return false;
*S.top++=e;
return true;
}
顺序栈的出栈
【算法步骤】
- 判断栈是否为空,若空则返回false
- 栈顶指针减一,栈顶元素出栈
//顺序栈的出栈
Status Pop(SqStack &S,SElemType &e)
{
if(S.top==S.base)
return false; //栈空
e=*--S.top;
return true;
}
取栈顶元素
【算法步骤】
- 若栈非空时,此操作返回当前栈顶元素值,栈顶指针不变
//取栈顶元素
SElemType GetTop(SqStack S)
{
if(S.base!=S.top)
return *(S.top-1);
}
注:由于顺序栈和顺序表一样,受到最大容量的限制,虽然可以在“满员”时重新分配空间扩大容量,但工作量较大,应该尽量避免。
链栈的表示和实现
通常链栈用单链表来表示,链栈的结点结构和单链表的结构相同。要了解单链表的基本操作请点我。
链栈的存储结构
typedef struct StackNode{
SElemType data; //数据域
struct StackNode *next; //指针域
}StackNode,*LinkStack;
- 由于栈的主要操作是在栈顶插入和删除,显然以链表的头部作为栈顶是最方便的,而且没必要附加一个头结点。
链栈的初始化
【算法步骤】
- 构造空栈,栈顶指针置为空即可
Status InitStack(LinkStack &S)
{
S=NULL;
return true;
}
链栈的入栈
【算法步骤】
- 为入栈元素e分配空间,用指针p指向
- 将新结点的数据域置为e
- 将新结点插入到栈顶
- 修改栈顶指针为p
Status Push(LinkStack &S,SElemType e)
{
StackNode *p=new StackNode; //生成新结点
p->data=e; //将结点的数据域赋值为e
p->next=S; //将新结点插入栈顶
S=p; //修改栈顶指针为p
return true;
}
链栈的出栈
【算法步骤】
- 判断栈是否为空,若空返回false
- 将栈顶数据域赋值给e
- 临时保存栈顶位置,以备释放
- 栈顶指针指向下一个地址
- 释放原栈顶指针,返回true
Status Pop(LinkStack &S,SElemType &e)
{
if(S==NULL)
return false;
e=S->data; //将栈顶赋给e
StackNode *p=S; //用p临时保存栈顶元素空间,以备释放
S=S->next; //修改栈顶指针
delete p; //释放原栈顶元素空间
return true;
}
取栈顶元素
【算法步骤】
- 如果栈顶不为空,返回栈顶数据域
SElemType GetTop(LinkStack S)
{
if(S)
return S->data;
}
遍历链栈
【算法步骤】
- 建立新指针p指向S(栈顶)
- 当p不为空时执行:你想要进行的步骤(可求长,可逐个销毁每个结点等等),p指向下一个地址
void StackTraverse(LinkStack S)
{
StackNode *p=S;
while(p)
{
此处可以写对每个结点进行的操作
p=p->next;
}
}
队列的基本操作请访问:https://blog.csdn.net/lesileqin/article/details/88410689
博客内容借鉴于:
①《数据结构》 作者:严蔚敏
②《大话数据结构》 作者:程杰