堆棧

堆疊的簡單示意圖
堆疊（stack）又稱為棧或-{zh-cn:堆疊; zh-tw:堆棧;}-，是計算機科學中的一種抽象資料型別，只允許在有序的線性資料集合的一端（稱為堆疊頂端，top）進行加入數據（push）和移除數據（pop）的運算。因而按照後進先出（LIFO, Last In First Out）的原理運作，堆疊常用一維數組或連結串列來實現。常與另一種有序的線性資料集合佇列相提並論。

**操作**
堆疊使用兩種基本操作：推入（壓棧，push）和彈出（彈棧，pop）：- 推入：將資料放入堆疊頂端，堆疊頂端移到新放入的資料。 - 彈出：將堆疊頂端資料移除，堆疊頂端移到移除後的下一筆資料。
上面就是棧最核心的兩個基本操作，可以在 棧的可視化頁面 date=20250401005604 中直觀理解這裡的操作。
棧的操作可視化圖片

**特點**
堆棧的基本特點：
# 先入後出，後入先出。
# 除頭尾節點之外，每個元素有一個前驅，一個後繼。

**抽象定義**
以下是堆棧的VDM（Vienna Development Method）：
函數簽名：
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");
}

**串列堆疊**

**存儲結構**
/* 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都有用作堆棧指針的寄存器。

**堆疊的應用**
 回溯法- 遞歸- 深度優先搜尋

**參考文獻**

**參見**
 連結串列- 佇列