For faster navigation, this Iframe is preloading the Wikiwand page for 堆栈.

堆栈

此条目需要补充更多来源。 (2020年5月24日)请协助补充多方面可靠来源改善这篇条目无法查证的内容可能会因为异议提出而被移除。致使用者:请搜索一下条目的标题(来源搜索:"堆栈"网页新闻书籍学术图像),以检查网络上是否存在该主题的更多可靠来源(判定指引)。
“堆栈”的各地常用名称
中国大陆堆栈、栈
台湾堆叠
堆栈的简单示意图

堆栈(stack)又称为堆叠,是电脑科学中的一种抽象资料类型,只允许在有序的线性资料集合的一端(称为堆栈顶端,top)进行加入数据(push)和移除数据(pop)的运算。因而按照后进先出(LIFO, Last In First Out)的原理运作,堆栈常用一维数组链接串列来实现。常与另一种有序的线性资料集合队列相提并论。

操作

堆栈使用两种基本操作:推入(压栈,push)和弹出(弹栈,pop):

  • 推入:将资料放入堆栈顶端,堆栈顶端移到新放入的资料。
  • 弹出:将堆栈顶端资料移除,堆栈顶端移到移除后的下一笔资料。

特点

堆栈的基本特点:

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

抽象定义

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

函数签名:

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

[2]

串列堆栈

存储结构

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

[2] 堆栈有时候也常用来指代堆栈段

硬件堆栈

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

硬件支持

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

堆栈的应用

参考文献

  1. ^ Jones: "Systematic Software Development Using VDM"
  2. ^ 2.0 2.1 高一凡. 《数据结构》算法实现及解析 2004年10月第2版. 西安: 西安电子科技大学出版社. ISBN 9787560611761 (中文). 

参见

{{bottomLinkPreText}} {{bottomLinkText}}
堆栈
Listen to this article

This browser is not supported by Wikiwand :(
Wikiwand requires a browser with modern capabilities in order to provide you with the best reading experience.
Please download and use one of the following browsers:

This article was just edited, click to reload
This article has been deleted on Wikipedia (Why?)

Back to homepage

Please click Add in the dialog above
Please click Allow in the top-left corner,
then click Install Now in the dialog
Please click Open in the download dialog,
then click Install
Please click the "Downloads" icon in the Safari toolbar, open the first download in the list,
then click Install
{{::$root.activation.text}}

Install Wikiwand

Install on Chrome Install on Firefox
Don't forget to rate us

Tell your friends about Wikiwand!

Gmail Facebook Twitter Link

Enjoying Wikiwand?

Tell your friends and spread the love:
Share on Gmail Share on Facebook Share on Twitter Share on Buffer

Our magic isn't perfect

You can help our automatic cover photo selection by reporting an unsuitable photo.

This photo is visually disturbing This photo is not a good choice

Thank you for helping!


Your input will affect cover photo selection, along with input from other users.

X

Get ready for Wikiwand 2.0 🎉! the new version arrives on September 1st! Don't want to wait?