[數據結構 - 第4章] 棧之鏈棧(C語言實現)


一、什么是鏈棧?

鏈棧:是指利用鏈式存儲結構實現的棧。

想想看棧只是棧頂來做插入和刪除操作,棧頂放在鏈棧的頭部還是尾部呢?由於單鏈表有頭指針,而棧頂指針也是必須的,那干嗎不讓它倆合二為一呢,所以比較好的辦法是把棧頂放在鏈棧的頭部(如下圖所示)。另外,都已經有了棧頂在頭部了,單鏈表中比較常用的頭結點也就失去了意義,通常對於鏈棧來說,是不需要頭結點的。


對於空棧來說,鏈表原定義是頭指針指向空,那么鏈棧的空其實就是 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章》 棧與隊列



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM