栈的定义
栈是限定仅在表尾进行插入和删除操作的线性表。
1、允许插入和删除的一端称为栈顶。
2、另一端称为栈底。
3、不包含任何数据元素的栈称为空栈。
4、栈又称为先进先出,简称LIFO结构。
5、栈是一种特殊的线性关系。
6、栈底是固定的,最先进栈的只能在栈底。
7、栈的插入操作,叫做进栈,也称为压栈、入栈。
8、栈的删除操作,叫做出栈、也有叫做弹栈。
栈的抽象数据类型
ADT 栈(stack)
Data
同线性表。元素具有相同的类型,相邻元素具有前驱和后继关系。
Operation
InitStack(*S):初始化操作,建立一个空栈S。
DestroyStack(*S):若栈存在,则销毁它。
ClearStack(*S):将栈清空。
StackEmpty(S):若栈为空,返回true,否则返回false。
GetTop(S,*e):若栈存在且非空,用e返回S的栈顶元素。
Push(*S,e):若栈S存在,插入新元素e到栈S中并成为栈顶元素。
Pop(*S,*e):删除栈S中栈顶元素,并用e返回其值。
StackLength(S):返回栈S的元素个数。
EndADT
栈的顺序存储结构及实现
栈的顺序存储结构
栈的结构定义
typedef int SELemType;//SELemType类型根据实际情况而定,这里假设为int
typedef struct
{
SELemType data[MAXSIZE];
int top;//用于栈顶指针
}SqStack
若现在有一个栈,StackSize是5,则栈普通情况、空栈和栈满的情况示意图如图所示。
栈的顺序存储结构-进栈操作
对于栈的插入,即进栈操作
插入元素e为新的栈顶元素
Status Push (SqStack *S,SELemType e)
{
if (S->top == MAXSIZE - 1) // 栈满
{
return ERROR;
}
S->top++; // 栈顶指针增加一
S->data[S->top]=e; //将新插入元素赋值给栈顶空间
return OK;
}
若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR。
Status Pop(SqStack *S, SELemType *e)
{
if(s->top == -1)
{
return ERROR;
}
*e = S->data[S->top]; //将要删除的栈顶元素赋值给e
S->top--; // 栈顶指针减一
retrun OK;
}
两栈共享空间结构
typedef struct
{
SELemType data[MAXSIZE];
int top1; //栈1 栈顶指针
int top2; //栈2 栈顶指针
}SqDoubleStack;
对于两栈共享空间的push方法,除了要插入元素值参数外,还需要有一个判断是栈1还是栈2的栈号参数stackNumber。插入元素的代码如下:
插入元素e为新的栈顶元素
Status Push (SqDoubleStack *S, SELemType e, int stackNumber)
{
if (S->top1+1 == S->top2) // 栈已满,不能再push新元素了
{
retrun ERROR;
}
if (stackNumber == 1) // 栈1 有元素进栈
{
S->data[++S->top1] = e;//若栈1 则先top+1后给数组元素赋值
}
else if (stackNumber == 2) //栈2 有元素进栈
{
S->data[--S->top2] = e;//若栈2 则先top2-1后给数组元素赋值
}
retrun OK;
}
若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
Status Pop(SqDoubleStack *S, SELemType *e, int stackNumber)
{
if (stackNumber == 1)
{
if(S->top1 == -1)
{
return ERROR;//说明栈1 以及是空栈,溢出
}
*e = S->data[S->top1--];//将栈1 的栈顶元素出栈
}
else if (stackNumber == 2)
{
if(S->top2 == MAXSIZE)
{
return ERROR;//说明栈2 已经是空栈,溢出
}
*e = S->data[S->top2++];//将栈2的栈顶元素出栈
}
return OK;
}
栈的链式存储结构及实现
栈的链式存储结构
1、栈的链式存储结构,简称为链栈。
2、把栈顶放在单链表的头部,单链表中比较常用的头结点也就失去意义了,通常对于链栈来说,是不需要头结点的。
3、对于链栈来说,基本不存在栈满的情况,处分内存已经没有可以使用的空间。
4、对于空栈来说,链表原定义是头指针指向空,那么链表的空其实就是top=NULL的时候。
链式的结构代码如下
Typedef struct StackNode
{
SELemType data;
struct StackNode *next;
}StackNode,*LinkStackPtr;
typedef struct LinkStack
{
LinkStackPtr top;
int count;
}LinkStack;
栈的链式存储结构---进栈操作
对于链栈的进栈push操作,假设元素值为e的新结点是s,top为栈顶指针,如图
插入元素e为新的栈顶元素
Status Push (LinkStack *S, SELemType e)
{
LinkStackPtr s = (LinkStackPtr)malloc(sizeof(StackNode));
s->data = e;
s->next = s->top;//把当前的栈顶元素赋值给新结点的直接后继
S->top = s;//将新的结点s赋值给栈顶指针
S->count++;
retrun OK;
}
栈的链式存储结构---出栈操作
至于链栈的出栈Pop操作,也是很简单的三句操作。假设变量p用来存储要删除的栈顶结点,将栈顶指针下移一位,最后释放p即可。
若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK,否则返回ERROR。
Status Pop(LinkStack *S, SELemType *e)
{
LinkStackPtr p;
if(StackEmpty(*S))
{
return ERROR;
}
*e = S->top->data;
p=S->top;//将栈顶结点赋值给p
S->top=S->top->next;//使得栈顶指针下移一位,指向后一结点
free(p);//释放结点p
S->count--;
return OK;
}
栈的作用
栈的引入简化了程序设计的问题,划分了不同关注层次,使得思考范围缩小,更加聚焦于我们要解决的问题核心。反之,像数组等,因为要分散精力去考虑数组的下标增减等细节问题,反而掩盖了问题的本质。