一、什么是鏈棧?
鏈棧:是指利用鏈式存儲結構實現的棧。
想想看棧只是棧頂來做插入和刪除操作,棧頂放在鏈棧的頭部還是尾部呢?由於單鏈表有頭指針,而棧頂指針也是必須的,那干嗎不讓它倆合二為一呢,所以比較好的辦法是把棧頂放在鏈棧的頭部(如下圖所示)。另外,都已經有了棧頂在頭部了,單鏈表中比較常用的頭結點也就失去了意義,通常對於鏈棧來說,是不需要頭結點的。
對於空棧來說,鏈表原定義是頭指針指向空,那么鏈棧的空其實就是 top=NULL 的時候。
鏈棧的結構代碼如下:
typedef int ElemType; /* ElemType類型根據實際情況而定,這里假設為int */
/* 鏈棧結點結構 */
typedef struct StackNode
{
ElemType data;
struct StackNode *next;
}StackNode;
/* 鏈棧結構 */
typedef struct
{
StackNode *top;
int count;
}LinkStack;
鏈棧的操作絕大部分都和單鏈表類似,只是在插入和刪除上,特殊一些。
順序棧與鏈棧的區別
兩者在時間復雜度上是一樣的,均為 O(1)。對於空間性能,順序棧需要事先確定一個固定的長度,可能會存在內存空間浪費的問題,但它的優勢是存取時定位很方便,而鏈棧則要求每個元素都有指針域,這同時也增加了一些內存開銷,但對於棧的長度無限制。所以它們的區別和線性表中討論的一樣,如果棧的使用過程中元素變化不可預料,有時很小,有時非常大,那么最好是用鏈棧,反之,如果它的變化在可控范圍內,建議使用順序棧會更好一些。
二、基本操作
2.1 初始化棧操作
實現代碼如下:
// 初始化棧操作
Status initStack(LinkStack **stack)
{
// 注意要給鏈棧分配內存
*stack = (LinkStack *)malloc(sizeof(LinkStack));
(*stack)->top = NULL; // 鏈棧的空其實就是 top=NULL 的時候
(*stack)->count = 0;
return TRUE;
}
2.2 進棧操作
對於鏈棧的進棧 push 操作,假設元素值為 e 的新結點是 s,top 為棧頂指針,示意圖如下圖所示:
實現代碼如下:
// 進棧操作
Status push(LinkStack *stack, ElemType e)
{
StackNode *s = (StackNode *)malloc(sizeof(StackNode));
s->data = e;
s->next = stack->top; // 把當前的棧頂元素賦值給新結點的直接后繼,見圖中①
stack->top = s; // 將新的結點s賦值給棧頂指針,見圖中②
stack->count++;
return TRUE;
}
2.3 出棧操作
至於鏈棧的出棧 pop 操作,也是很簡單的三句操作。假設變量 p 用來存儲要刪除的棧頂結點,將棧頂指針下移一位,最后釋放 p 即可,如下圖所示:
// 出棧操作
Status pop(LinkStack *stack, ElemType *e)
{
StackNode *p;
if (isEmpty(stack))
return FALSE;
*e = stack->top->data;
p = stack->top; // p用來存儲要刪除的棧頂結點,見圖中③
stack->top = stack->top->next; // 使得棧頂指針下移一位,指向后一結點,見圖中④
free(p); // 釋放結點p
stack->count--;
return TRUE;
}
鏈棧的進棧 push 和出棧 pop 操作都很簡單,沒有任何循環操作,時間復雜度均為 O(1)。
2.4 遍歷棧操作
實現代碼如下:
// 遍歷棧操作
Status traverseStack(LinkStack *stack)
{
StackNode *p;
p = stack->top;
while (p)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
return TRUE;
}
三、完整程序
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20 /* 存儲空間初始分配量 */
typedef int Status;
typedef int ElemType; /* ElemType類型根據實際情況而定,這里假設為int */
/* 鏈棧結點結構 */
typedef struct StackNode
{
ElemType data;
struct StackNode *next;
}StackNode;
/* 鏈棧結構 */
typedef struct
{
StackNode *top;
int count;
}LinkStack;
Status initStack(LinkStack **stack); // 初始化棧操作
Status push(LinkStack *stack, const ElemType e); // 進棧操作
Status pop(LinkStack *stack, ElemType *e); // 出棧操作
Status traverseStack(LinkStack *stack); // 遍歷棧操作
Status clearStack(LinkStack *stack); // 清空棧操作
Status isEmpty(LinkStack *stack); // 判斷是否為空
Status getTop(LinkStack *stack, ElemType *e); // 獲得棧頂元素
int getLength(LinkStack *stack); // 獲取棧的長度
// 初始化棧操作
Status initStack(LinkStack **stack)
{
// 注意要給鏈棧分配內存
*stack = (LinkStack *)malloc(sizeof(LinkStack));
(*stack)->top = NULL; // 鏈棧的空其實就是 top=NULL 的時候
(*stack)->count = 0;
return TRUE;
}
// 進棧操作
Status push(LinkStack *stack, ElemType e)
{
StackNode *s = (StackNode *)malloc(sizeof(StackNode));
s->data = e;
s->next = stack->top; // 把當前的棧頂元素賦值給新結點的直接后繼,見圖中①
stack->top = s; // 將新的結點s賦值給棧頂指針,見圖中②
stack->count++;
return TRUE;
}
// 出棧操作
Status pop(LinkStack *stack, ElemType *e)
{
StackNode *p;
if (isEmpty(stack))
return FALSE;
*e = stack->top->data;
p = stack->top; // p用來存儲要刪除的棧頂結點,見圖中③
stack->top = stack->top->next; // 使得棧頂指針下移一位,指向后一結點,見圖中④
free(p); // 釋放結點p
stack->count--;
return TRUE;
}
// 遍歷棧操作
Status traverseStack(LinkStack *stack)
{
StackNode *p;
p = stack->top;
while (p)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
return TRUE;
}
// 清除棧操作
Status clearStack(LinkStack *stack)
{
StackNode *p;
StackNode *q;
p = stack->top;
while (p)
{
q = p;
p = p->next;
free(q);
}
stack->count = 0;
return TRUE;
}
// 判斷是否為空棧
Status isEmpty(LinkStack *stack)
{
return stack->count == 0 ? TRUE : FALSE;
}
// 獲得棧頂元素
Status getTop(LinkStack *stack, ElemType *e)
{
if (stack->top == NULL)
return FALSE;
else
*e = stack->top->data;
return TRUE;
}
// 獲得棧的長度
int getLength(LinkStack *stack)
{
return stack->count;
}
int main()
{
// 初始化棧
LinkStack *stack;
if (initStack(&stack) == TRUE)
printf("初始化鏈棧成功!\n\n");
// 入棧操作
for (int j = 1; j <= 10; j++)
push(stack, j);
printf("入棧操作(0-10)!\n\n");
// 出棧操作
int e;
pop(stack, &e);
printf("彈出的棧頂元素e=%d\n\n", e);
// 遍歷棧
printf("遍歷棧,棧中元素依次為:");
traverseStack(stack);
printf("\n");
// 獲得棧頂元素
getTop(stack, &e);
printf("棧頂元素 e=%d 棧的長度為%d\n\n", e, getLength(stack));
// 判斷是否為空棧
printf("棧空否:%d(1:空 0:否)\n\n", isEmpty(stack));
// 清空棧
clearStack(stack);
printf("清空棧后,棧空否:%d(1:空 0:否)\n\n", isEmpty(stack));
return 0;
}
輸出結果如下圖所示:
參考:
《大話數據結構 - 第4章》 棧與隊列