堆疊(stack)又稱為棧或-{zh-cn:堆叠; zh-tw:堆棧;}-,是计算机科學中的一種抽象資料型別,只允許在有序的線性資料集合的一端(稱為堆疊頂端,top)進行加入数据(push)和移除数据(pop)的運算。因而按照後進先出(LIFO, Last In First Out)的原理運作,堆疊常用一維数组或連結串列來實現。常與另一種有序的線性資料集合佇列相提並論。
操作
堆疊使用兩種基本操作:推入(压栈,push)和彈出(弹栈,pop):
- 推入:將資料放入堆疊頂端,堆疊頂端移到新放入的資料。
- 彈出:將堆疊頂端資料移除,堆疊頂端移到移除後的下一筆資料。
特点
堆栈的基本特点:
先入后出,后入先出。
除头尾节点之外,每个元素有一个前驱,一个后继。
抽象定义
以下是堆栈的VDM(**'):
函数签名:
init: -> Stack
push: N x Stack -> Stack
top: Stack -> (N \cup ERROR)
pop: Stack -> Stack
isempty: Stack -> Boolean
此处的N代表某个元素(如自然数),而\cup表示集合求并。
语义:
top(init()) = ERROR
top(push(i,s)) = i
pop(init()) = init()
pop(push(i, s)) = s
isempty(init()) = true
isempty(push(i, s)) = false
软件堆栈
堆栈可以用数组和链表两种方式实现,一般为一个堆栈预先分配一个大小固定且较合适的空间并非难事,所以较流行的做法是Stack结构下含一个数组。如果空间实在紧张,也可用链表实现,且去掉表头。
这里的例程是以C語言实现的。
陣列堆疊
存储结构
/ c3-1.h 栈的顺序存储表示 /
#define STACK_INIT_SIZE 10 / 存储空间初始分配量 /
#define STACK_INCREMENT 2 / 存储空间分配增量 /
typedef struct SqStack
{
SElemType base; / 在栈构造之前和销毁之后,base的值为NULL */
SElemType top; / 栈顶指针 */
int stacksize; / 当前已分配的存储空间,以元素为单位 /
}SqStack; / 顺序栈 /
基本操作
/ bo3-1.c 顺序栈(存储结构由c3-1.h定义)的基本操作(9个) /
void InitStack(SqStack *S)
{ / 构造一个空栈S /
(S).base=(SElemType )malloc(STACK_INIT_SIZE*sizeof(SElemType));
if(!(*S).base)
exit(OVERFLOW); / 存储分配失败 /
(S).top=(S).base;
(*S).stacksize=STACK_INIT_SIZE;
}
void DestroyStack(SqStack *S)
{ / 销毁栈S,S不再存在 /
free((*S).base);
(*S).base=NULL;
(*S).top=NULL;
(*S).stacksize=0;
}
void ClearStack(SqStack *S)
{ / 把S置为空栈 /
(S).top=(S).base;
}
Status StackEmpty(SqStack S)
{ / 若栈S为空栈,则返回TRUE,否则返回FALSE /
if(S.top==S.base)
return TRUE;
else
return FALSE;
}
int StackLength(SqStack S)
{ / 返回S的元素个数,即栈的长度 /
return S.top-S.base;
}
Status GetTop(SqStack S,SElemType *e)
{ / 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR /
if(S.top>S.base)
{
e=(S.top-1);
return OK;
}
else
return ERROR;
}
void Push(SqStack *S,SElemType e)
{ / 插入元素e为新的栈顶元素 /
if((S).top-(S).base>=(S).stacksize) / 栈满,追加存储空间 */
{
(S).base=(SElemType )realloc((S).base,((S).stacksize+STACK_INCREMENT)*sizeof(SElemType));
if(!(*S).base)
exit(OVERFLOW); / 存储分配失败 /
(S).top=(S).base+(*S).stacksize;
(*S).stacksize+=STACK_INCREMENT;
}
((S).top)++=e;
}
Status Pop(SqStack S,SElemType e)
{ / 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR /
if((S).top==(S).base)
return ERROR;
e=--(*S).top;
return OK;
}
void StackTraverse(SqStack S,void(*visit)(SElemType))
{ / 从栈底到栈顶依次对栈中每个元素调用函数visit() /
while(S.top>S.base)
visit(*S.base++);
printf("\n");
}
串列堆疊
存储结构
/ c2-2.h 线性表的单链表存储结构 /
struct LNode
{
ElemType data;
struct LNode *next;
};
typedef struct LNode LinkList; / 另一种定义LinkList的方法 */
基本操作
/ bo3-5.c 链栈(存储结构由c2-2.h定义)的基本操作(4个) /
/ 部分基本操作是由bo2-8.cpp中的函数改名得来 /
/ 另一部分基本操作是由调用bo2-8.cpp中的函数(取特例)得来 /
typedef SElemType ElemType; / 栈结点类型和链表结点类型一致 /
#include"c2-2.h" / 单链表存储结构 /
typedef LinkList LinkStack; / LinkStack是指向栈结点的指针类型 /
#define InitStack InitList / InitStack()与InitList()作用相同,下同 /
#define DestroyStack DestroyList
#define ClearStack ClearList
#define StackEmpty ListEmpty
#define StackLength ListLength
#include"bo2-8.c" / 无头结点单链表的基本操作 /
Status GetTop(LinkStack S,SElemType *e)
{ / 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR /
return GetElem(S,1,e);
}
Status Push(LinkStack *S,SElemType e)
{ / 插入元素e为新的栈顶元素 /
return ListInsert(S,1,e);
}
Status Pop(LinkStack S,SElemType e)
{ / 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR /
return ListDelete(S,1,e);
}
void StackTraverse(LinkStack S,void(*visit)(SElemType))
{ / 从栈底到栈顶依次对栈中每个元素调用函数visit() /
LinkStack temp,p=S; / p指向栈顶元素 /
InitStack(&temp); / 初始化临时栈temp /
while(p)
{
Push(&temp,p->data); / 由S栈顶到栈底,依次将栈元素入栈到temp栈 /
p=p->next;
}
ListTraverse(temp,visit); / 遍历temp线性表 /
}
链表基本操作
/ bo2-8.c 不带头结点的单链表(存储结构由c2-2.h定义)的部分基本操作(9个) /
#define DestroyList ClearList / DestroyList()和ClearList()的操作是一样的 /
void InitList(LinkList *L)
{ / 操作结果:构造一个空的线性表L /
L=NULL; / 指针为空 */
}
void ClearList(LinkList *L)
{ / 初始条件:线性表L已存在。操作结果:将L重置为空表 /
LinkList p;
while(L) / L不空 */
{
p=L; / p指向首元结点 */
L=(L)->next; / L指向第2个结点(新首元结点) /
free(p); / 释放首元结点 /
}
}
Status ListEmpty(LinkList L)
{ / 初始条件:线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE /
if(L)
return FALSE;
else
return TRUE;
}
int ListLength(LinkList L)
{ / 初始条件:线性表L已存在。操作结果:返回L中数据元素个数 /
int i=0;
LinkList p=L;
while(p) / p指向结点(没到表尾) /
{
p=p->next; / p指向下一个结点 /
i++;
}
return i;
}
Status GetElem(LinkList L,int i,ElemType *e)
{ / L为不带头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR /
int j=1;
LinkList p=L;
if(inext;
}
if(j==i) / 存在第i个元素 /
{
*e=p->data;
return OK;
}
else
return ERROR;
}
int LocateElem(LinkList L,ElemType e,Status(*compare)(ElemType,ElemType))
{ / 初始条件:线性表L已存在,compare()是数据元素判定函数(满足为1,否则为0) /
/ 操作结果:返回L中第1个与e满足关系compare()的数据元素的位序。 /
/ 若这样的数据元素不存在,则返回值为0 /
int i=0;
LinkList p=L;
while(p)
{
i++;
if(compare(p->data,e)) / 找到这样的数据元素 /
return i;
p=p->next;
}
return 0;
}
Status ListInsert(LinkList *L,int i,ElemType e)
{ / 在不带头结点的单链线性表L中第i个位置之前插入元素e /
int j=1;
LinkList p=*L,s;
if(idata=e; / 给s的data域赋值 /
if(i==1) / 插在表头 /
{
s->next=*L;
L=s; / 改变L */
}
else
{ / 插在表的其余处 /
while(p&&jnext;
j++;
}
if(!p) / i大于表长+1 /
return ERROR;
s->next=p->next;
p->next=s;
}
return OK;
}
Status ListDelete(LinkList L,int i,ElemType e)
{ / 在不带头结点的单链线性表L中,删除第i个元素,并由e返回其值 /
int j=1;
LinkList p=*L,q;
if(i==1) / 删除第1个结点 /
{
L=p->next; / L由第2个结点开始 */
*e=p->data;
free(p); / 删除并释放第1个结点 /
}
else
{
while(p->next&&jnext;
j++;
}
if(!p->next||j>i-1) / 删除位置不合理 /
return ERROR;
q=p->next; / 删除并释放结点 /
p->next=q->next;
*e=q->data;
free(q);
}
return OK;
}
void ListTraverse(LinkList L,void(*vi)(ElemType))
{ / 初始条件:线性表L已存在。操作结果:依次对L的每个数据元素调用函数vi() /
LinkList p=L;
while(p)
{
vi(p->data);
p=p->next;
}
printf("\n");
}
堆棧有時候也常用來指代堆棧段。
硬件堆栈
架构层次上的堆栈通常被用以申请和访问内存。
硬件支持
大多数CPU都有用作堆栈指针的寄存器。
堆疊的應用
- 回溯法
- 递归
- 深度優先搜尋
参考文献
参见
- 連結串列
- 佇列
评论 (0)