一、栈的定义
栈是一种只能在一段进行插入或删除操作的特殊线性表(类似于一个瓶子,只能从瓶口放入或倒出内容物)。表中允许进行插入、删除操作的一端称为栈顶(类似瓶口),另一端则称为栈底(瓶底)。栈顶的位置是动态的,由栈顶指针指示。当栈中没有元素时称为空栈,栈的插入操作称为进栈或入栈,删除操作称为退栈或出栈。
栈中元素逻辑关系与线性表的相同,栈可以采用与线性表相同的存储结构。
区别于普通线性表可以在任意位置进行插入删除操作的特点,栈的主要特点为“后进先出”,即后进栈的元素先出栈(靠近瓶口的内容物先被倒出)。因此栈也被称为后进先出表。需要注意的是,每次进栈的元素都放在原来的栈顶元素之前成为新的栈顶元素,每次出栈的元素都是当前的栈顶元素。
栈的抽象数据类型定义如下:
ADT Stack
{
D={ai|1<=i<=n,n>=0,ai为ELement类型}
R={<ai,ai+1>|ai~ai+1∈D,i=1,...,n-1}
InitStack(&s):初始化
DestoryStack(&s):销毁栈
StackEmpty(s):P判断栈是否为空
Push(&s,e):进栈
Pop(&s,&e):出栈
GetTop(s,&e):获取栈顶元素
}
二、栈的顺序存储结构及其基本运算的实现
1、结构体定义
typedef struct
{
ElemType data[MaxSize];
int top;//栈顶指针
} SqStack;
注意 :
1.top总是指向栈顶元素,初始值为-1(即栈空时s->top==-1)
2.进栈时先top+1,然后将元素e放在栈顶指针处;出栈时先将指针top处的元素取出存放在e中,然后将top-1。
3.当top=MaxSize-1时栈满,不能再进栈(栈满时s->top==MaxSize-1)
2、顺序栈的基本运算
(1)初始化栈
SqStack *InitSqStack
{
SqStack*s=(SqStack*)malloc(sizeof(SqStack));
s->top=-1;
return s;
}
(2)销毁栈
void DestoryStack(Sqstack*s){
free(s);
}
(3)判断栈是否为空
bool StackEmpty(SqStack*s)
{
return s->top==-1;
}
(4)进栈
bool Push(SqStack*s,ELemType e)
{
if(s->top==MaxSize-1)
return false;
s->top++;//先改变top所指位置
s->data[s->top]=e;//再将数据e赋值给top所指位置
return true;
}
(5)出栈
bool Pop(SqStack*s,ELemType *e)
{
if(s->top==-1)
return false;
*e=s->data[s->top];
s->top--;
return true;
}
(6)获取栈顶元素
bool GetTop(SqStack*s,ELemType *e)
{
if(s->top==-1)
return false;
*e=s->data[s->top];
return true;
}
三、栈的链式存储结构及其基本运算的实现
采用链表存储的栈称为链栈。链栈有多种,这里采用带头结点的单链表实现。
1.结构体定义
typedef struct linknode
{ ElenType data;
struct linknode *next;
}LinkStNode;
注意:
1.栈空的条件:s->next==NULL
2.只有内存溢出时才出现栈满,故可以视为不存在栈满
3.元素进栈需要先建立一个结点用于存放元素(由p指向它),将结点p插入头结点之后
4.出栈操作为取出头结点(即栈顶)的data值并删除
2、链栈的基本运算
(1)初始化栈
LinkStNode *InitSqStack
{
LinkStNode*s=(LinkStNode*)malloc(sizeof(LinkStNode));
s->next=NULL;
return s;
}
(2)销毁栈
void DestoryStack(LinkStNode*s)
{
LinkStNode *pre=s,*p=s->next;//pre指向头结点,p指向首节点
while(p!=NULL)//循环到首节点p为空
{
free(pre);//释放pre结点
pre=p;
p=pre->next;//pre、p同步后移
}
free pre;//此时pre指向尾节点,释放其空间
}
(3)判断栈是否为空
bool StackEmpty(LinkStNode*s)
{
return s->top==NULL;
}
(4)进栈
bool Push(LinkStNode*s,ELemType e)
{
LinkStNode*p;
p=(LinkStNode*)malloc(sizeof(LinkStnode));//新建结点p
p->data=e;//存放元素e
p->next=s->next;//将p结点插入作为首结点
s->next=p;
return true;
}
(5)出栈
bool Pop(LinkStNode*s,ELemType *e)
{
LinkStNode*p;
if(s->next==NULL)
return false;
p=s->next;//p指向首结点
e=p->data;//另存首结点值
s->next=p->next;用下一个结点覆盖首结点(删除当前首结点)
free(p);
return true;
}
(6)获取栈顶元素
bool GetTop(LinkStNode*s,Elemtype*e)
{
if(s->next==NULL)
return false;
*e=s->next->data;
return true;
}
注意:用链表表示栈时,首结点才是栈顶,头结点进作为链表操作的统一接口,是在链表的第一个有效元素结点之前附设的一个结点,它不是链表的有效数据结点。