堆栈

「堆栈」的各地常用名稱
中国大陸堆栈、栈
臺灣堆疊
堆疊的简单示意图

堆疊(stack)又稱為堆叠,是计算机科學中的一種抽象資料型別,只允許在有序的線性資料集合的一端(稱為堆疊頂端,top)進行加入数据(push)和移除数据(pop)的運算。因而按照後進先出(LIFO, Last In First Out)的原理運作,堆疊常用一維数组連結串列來實現。常與另一種有序的線性資料集合佇列相提並論。

操作

[编辑]

堆疊使用兩種基本操作:推入(压栈,push)和彈出(弹栈,pop):

  • 推入:將資料放入堆疊頂端,堆疊頂端移到新放入的資料。
  • 彈出:將堆疊頂端資料移除,堆疊頂端移到移除後的下一筆資料。

上面就是栈最核心的两个基本操作,可以在栈的可视化页面中直观理解这里的操作。[1]

栈的操作可视化图片

特点

[编辑]

堆栈的基本特点:

  1. 先入后出,后入先出。
  2. 除头尾节点之外,每个元素有一个前驱,一个后继。

抽象定义

[编辑]

以下是堆栈的VDM(Vienna Development Method英语Vienna Development Method):[2]

函数签名:

  init: -> Stack   push: N x Stack -> Stack   top: Stack -> (N  ERROR)   pop: Stack -> Stack   isempty: Stack -> Boolean 

此处的N代表某个元素(如自然数),而表示集合求并。

语义:

  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"); } 

[3]

串列堆疊

[编辑]

存储结构

[编辑]
/* 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(i<1) /* i值不合法 */ 		return ERROR; 	while(j<i&&p) /* 没到第i个元素,也没到表尾 */ 	{ 		j++; 		p=p->next; 	} 	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(i<1) /* i值不合法 */ 		return ERROR; 	s=(LinkList)malloc(sizeof(struct LNode)); /* 生成新结点 */ 	s->data=e; /* 给s的data域赋值 */ 	if(i==1) /* 插在表头 */ 	{ 		s->next=*L; 		*L=s; /* 改变L */ 	} 	else 	{	/* 插在表的其余处 */ 		while(p&&j<i-1) /* 寻找第i-1个结点 */ 		{ 			p=p->next; 			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&&j<i-1) /* 寻找第i个结点,并令p指向其前驱 */ 		{ 			p=p->next; 			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"); } 

[3] 堆棧有時候也常用來指代堆棧段

硬件堆栈

[编辑]

架构层次上的堆栈通常被用以申请和访问内存。

硬件支持

[编辑]

大多数CPU都有用作堆栈指针的寄存器。

堆疊的應用

[编辑]

参考文献

[编辑]
  1. ^ 栈的可视化页面: 在线可视化演示栈的各种操作
  2. ^ Jones: "Systematic Software Development Using VDM"
  3. ^ 3.0 3.1 高一凡. 《数据结构》算法实现及解析 2004年10月第2版. 西安: 西安电子科技大学出版社. ISBN 9787560611761 (中文). 

参见

[编辑]