C/C++ 入門算法,“棧”的全面剖析

棧的原理精講

棧是一種線性結構,即線性排列。
好比如一條衚衕:
在這裏插入圖片描述
先進來的車子只能等後面進來的車子出去後,他才能出去,也就體現了棧的特性:“後進先出”!

棧也是一種線性表,只不過它是操作受限的線性表,只能在一端操作。

進出的一端稱爲棧頂(top),另一端稱爲棧底(base)。棧可以用順序存儲,也可以用鏈式存儲。我們先看順序存儲方式:
在這裏插入圖片描述
其中,base 指向棧底,top 指向棧頂。

注意:棧只能在一端操作,後進先出,這是棧的關鍵特徵,也就是說不允許在中間查找、取值、插入、刪除等操作,我們掌握好順序棧的初始化、入棧,出棧,取棧頂元素等操作即可。

棧頂永遠都是指向下一個待插入元素的位置!!!


順序棧的算法實現

代碼中所用到的所有接口:

bool initStack(Stack &stack);	// 初始化棧
bool estimateStackEmpty(Stack &stack);	// 判斷是否爲空
bool estimateStackFull(Stack &stack);	// 判斷是否已滿
int stackSize(Stack &stack);	// 獲取棧已存儲的元素個數
bool insertStack(Stack &stack, ElemType value); // 入棧
bool popStack(Stack &stack, ElemType &vlaue);	// 出棧
bool gainStack(Stack &stack, ElemType& value);	// 獲取棧頂的元素
void deleteStack(Stack &stack);	// 釋放棧的內存

棧數據結構的定義
#define StackMax 128	// 棧最大可以存放的元素個數

typedef int ElemType;	// 棧存儲的數據類型

// 建棧
typedef struct _SqStack {
	ElemType *top;	// 棧頂指針
	ElemType *base;	// 棧底指針
}Stack;

棧的初始化

爲棧底base分配內存,然後top,base都指向這塊內存!
在這裏插入圖片描述

bool initStack(Stack &stack) {
	stack.base = new ElemType[StackMax];	// 分配內存(棧底指向它)
	if (!stack.base) {	// 判斷是否分配內存失敗
		return false;
	}

	stack.top = stack.base;	// 棧頂棧底指向同一個位置
	return true;
}

檢測棧是否爲空 && 檢測棧是否已滿

當棧頂與棧尾相等時,即棧爲空!(由上面棧的初始化可知)

bool estimateStackEmpty(Stack &stack) {
	if (stack.top == stack.base) {	// 當棧頂棧底指向同一個位置,棧爲空。
		return true;
	}
	return false;
}

當棧頂減去棧尾等於宏定義StackMax時,即棧已滿!

bool estimateStackFull(Stack &stack) {	// 指針的特性:指針一減去指針二,可以得出他們
	if ((stack.top - stack.base) == StackMax) {	// 之中相隔幾個元素。
		return true;					// 當與棧所能存儲的最大值相等時,爲滿。
	}
	return false;
}

入棧

入棧操作:判斷是否棧滿,如果棧已滿,則入棧失敗,否則將元素放入棧頂,棧頂指針向上移動一個空間(top++)。

棧頂永遠都是指向下一個待插入元素的位置!!!
在這裏插入圖片描述

bool insertStack(Stack &stack, ElemType value) {
	if (estimateStackFull(stack)) {
		cout << "棧已滿!" << endl;
		return false;
	}

	*stack.top = value;	// 棧頂指針解引用後,value值賦值給它存儲
	stack.top++;		// 棧頂指針只增一

	return true;
}

出棧

出棧操作: 和入棧相反,出棧前要判斷是否棧空,如果棧是空的,則出棧失敗,否則將棧頂自減一後(top–),減一後的棧頂指向的內存中的元素暫存給一個變量返回。
在這裏插入圖片描述

// 棧頂元素出棧後,保存出棧元素返回
bool popStack(Stack &stack, ElemType &value) {
	if (estimateStackEmpty(stack)) {
		cout << "棧爲空!" << endl;
		return false;
	}

	stack.top--;	// 棧頂指針自減一
	value = *stack.top;	// 將棧頂指針所在位置的值賦值給value返回

	return true;
}

獲取棧頂元素

取棧頂元素和出棧不同,取棧頂元素只是把棧頂元素複製一份,棧的元素個數不變,而出棧是指棧頂元素取出,棧內不再包含這個元素。
在這裏插入圖片描述

bool gainStack(Stack &stack, ElemType& value) {
	if (estimateStackEmpty(stack)) {
		cout << "棧爲空!" << endl;
		return false;
	}

	value = *(stack.top - 1);	// 將棧頂指針“假”減一後的值賦值給value返回

	return true;
}

清空棧

釋放內存

void deleteStack(Stack &stack) {
	if (stack.base) {
		delete stack.base;
		stack.top = NULL;
		stack.base = NULL;
		cout << "棧已釋放內存!" << endl;
	}
}

獲取棧存儲的元素個數

棧頂減去棧尾即爲棧存儲的元素個數!

int stackSize(Stack &stack) {	// 指針的特性:指針一減去指針二,可以得出他們之中相隔幾個元素。
	return stack.top - stack.base;
}

完整源碼實現
#include <iostream>
#include <Windows.h>

using namespace std;

#define StackMax 128	// 棧最大可以存放的元素個數

typedef int ElemType;	// 棧存儲的數據類型

// 建棧
typedef struct _SqStack {
	ElemType *top;	// 棧頂指針
	ElemType *base;	// 棧底指針
}Stack;

bool initStack(Stack &stack);	// 初始化棧
bool estimateStackEmpty(Stack &stack);	// 判斷是否爲空
bool estimateStackFull(Stack &stack);	// 判斷是否已滿
int stackSize(Stack &stack);	// 獲取棧已存儲的元素個數
bool insertStack(Stack &stack, ElemType value); // 入棧
bool popStack(Stack &stack, ElemType &vlaue);	// 出棧
bool gainStack(Stack &stack, ElemType& value);	// 獲取棧頂的元素
void deleteStack(Stack &stack);	// 釋放棧的內存


bool initStack(Stack &stack) {
	stack.base = new ElemType[StackMax];	// 分配內存(棧底指向它)
	if (!stack.base) {	// 判斷是否分配內存失敗
		return false;
	}

	stack.top = stack.base;	// 棧頂棧底指向同一個位置
	return true;
}

bool estimateStackEmpty(Stack &stack) {
	if (stack.top == stack.base) {	// 當棧頂棧底指向同一個位置,棧爲空。
		return true;
	}
	return false;
}


bool estimateStackFull(Stack &stack) {	// 指針的特性:指針一減去指針二,可以得出他們
	if ((stack.top - stack.base) == StackMax) {	// 之中相隔幾個元素。
		return true;					// 當與棧所能存儲的最大值相等時,爲滿。
	}
	return false;
}


int stackSize(Stack &stack) {	// 指針的特性:指針一減去指針二,可以得出他們之中相隔幾個元素。
	return stack.top - stack.base;
}


bool insertStack(Stack &stack, ElemType value) {
	if (estimateStackFull(stack)) {
		cout << "棧已滿!" << endl;
		return false;
	}

	*stack.top = value;	// 棧頂指針解引用後,value值賦值給它存儲
	stack.top++;		// 棧頂指針只增一

	return true;
}


// 棧頂元素出棧後,保存出棧元素返回
bool popStack(Stack &stack, ElemType &value) {
	if (estimateStackEmpty(stack)) {
		cout << "棧爲空!" << endl;
		return false;
	}

	stack.top--;	// 棧頂指針自減一
	value = *stack.top;	// 將棧頂指針所在位置的值賦值給value返回

	return true;
}


bool gainStack(Stack &stack, ElemType& value) {
	if (estimateStackEmpty(stack)) {
		cout << "棧爲空!" << endl;
		return false;
	}

	value = *(stack.top - 1);	// 將棧頂指針“假”減一後的值賦值給value返回

	return true;
}


void deleteStack(Stack &stack) {
	if (stack.base) {
		delete stack.base;
		stack.top = NULL;
		stack.base = NULL;
		cout << "棧已釋放內存!" << endl;
	}
}


int main(void) {
	Stack stack;

	// 初始化棧
	initStack(stack);

	// 插入元素
	int n = 0;
	int value = 0;
	cout << "請輸入需要插入的元素個數:";
	cin >> n;

	while (n > 0) {
		cin >> value;
		insertStack(stack, value);
		n--;
	}

	// 獲取棧頂的元素
	gainStack(stack, value);
	cout << "當前棧頂的元素是:" << value << endl;

	// 獲取棧的元素個數
	cout << "當前棧的元素個數是:" << stackSize(stack) << endl;


	// 出棧
	cout << "出棧順序:" << endl;
	while (!estimateStackEmpty(stack)) {
		popStack(stack, value);
		cout << value << " ";
	}
	cout << endl;

	// 釋放棧的內存
	deleteStack(stack);

	system("pause");
	return 0;
}

運行截圖:
在這裏插入圖片描述


課後思考:

如果把棧的結構體定義改爲如下形式,該如何實現操作棧的算法?

typedef struct _SqStack {
	int top;		// 棧頂的位置
	ElemType* base;	// 棧底指針
}Stack;

=

=

=

具體實現:

#include <iostream>
#include <Windows.h>

using namespace std;

#define StackMax 128	// 棧最大可以存放的元素個數

typedef int ElemType;	// 棧存儲的數據類型

// 建棧
typedef struct _SqStack {
	int top;		// 棧頂的位置
	ElemType* base;	// 棧底指針
}Stack;

bool initStack(Stack& stack);	// 初始化棧
bool estimateStackEmpty(Stack& stack);	// 判斷是否爲空
bool estimateStackFull(Stack& stack);	// 判斷是否已滿
int stackSize(Stack& stack);	// 獲取棧已存儲的元素個數
bool insertStack(Stack& stack, ElemType value); // 入棧
bool popStack(Stack& stack, ElemType& vlaue);	// 出棧
bool gainStack(Stack& stack, ElemType& value);	// 獲取棧頂的元素
void deleteStack(Stack& stack);	// 釋放棧的內存


bool initStack(Stack& stack) {
	stack.base = new ElemType[StackMax];	// 分配內存(棧底指向它)
	if (!stack.base) {	// 判斷是否分配內存失敗
		return false;
	}

	stack.top = 0;
	return true;
}

bool estimateStackEmpty(Stack& stack) {
	if (stack.top == 0) {	// 當棧頂位置等於零,爲空
		return true;
	}
	return false;
}


bool estimateStackFull(Stack& stack) {		
	if (stack.top == StackMax) {	// 當棧頂位置等於棧所能存儲的最大值時,爲滿
		return true;					
	}
	return false;
}


int stackSize(Stack& stack) {	
	return stack.top;	// 棧頂位置就是元素的個數
}


bool insertStack(Stack& stack, ElemType value) {
	if (estimateStackFull(stack)) {
		cout << "棧已滿!" << endl;
		return false;
	}

	stack.base[stack.top] = value;	// 以棧頂位置作爲下標存儲數據
	stack.top++;	// 棧頂自增一

	return true;
}


// 棧頂元素出棧後,保存出棧元素返回
bool popStack(Stack& stack, ElemType& value) {
	if (estimateStackEmpty(stack)) {
		cout << "棧爲空!" << endl;
		return false;
	}

	stack.top--;	// 棧頂自減一
	value = stack.base[stack.top];	// 以棧頂位置作爲下標的值賦值給value返回

	return true;
}


bool gainStack(Stack& stack, ElemType& value) {
	if (estimateStackEmpty(stack)) {
		cout << "棧爲空!" << endl;
		return false;
	}

	value = stack.base[stack.top - 1];	// 將棧頂下標“假”減一後的值賦值給value返回

	return true;
}


void deleteStack(Stack& stack) {
	if (stack.base) {
		delete stack.base;
		stack.top = 0;
		stack.base = NULL;
		cout << "棧已釋放內存!" << endl;
	}
}


int main(void) {
	Stack stack;

	// 初始化棧
	initStack(stack);

	// 插入元素
	int n = 0;
	int value = 0;
	cout << "請輸入需要插入的元素個數:";
	cin >> n;

	while (n > 0) {
		cin >> value;
		insertStack(stack, value);
		n--;
	}

	// 獲取棧頂的元素
	gainStack(stack, value);
	cout << "當前棧頂的元素是:" << value << endl;

	// 獲取棧的元素個數
	cout << "當前棧的元素個數是:" << stackSize(stack) << endl;


	// 出棧
	cout << "出棧順序:" << endl;
	while (!estimateStackEmpty(stack)) {
		popStack(stack, value);
		cout << value << " ";
	}
	cout << endl;

	// 釋放棧的內存
	deleteStack(stack);

	system("pause");
	return 0;
}

在這裏插入圖片描述


課後作業:棧的鏈式存儲結構

棧其實是可以使用鏈表來實現的,具體實現給大家自己去思考啦!

=

=

=

剛開始小編首先想到的是使用雙向鏈表來實現,但是這樣實屬殺雞使用宰牛刀!

於是乎再進一步思考了一會,發現單向循環鏈表也可以做!
使用單向循環鏈表實現圖解:

在這裏插入圖片描述

但是還可以更加簡便,那就是使用單向鏈表來實現!

希望下面兩幅圖片可以助你理解:使用單向鏈表實現

在這裏插入圖片描述
在這裏插入圖片描述

單向鏈表實現棧 代碼:

#include <iostream>
#include <Windows.h>

using namespace std;

typedef int DateType;

// 循環單項鍊表
typedef struct _linkStack {
	DateType date;
	int size;					// 統計棧的個數
	struct _linkStack *next;	// 頭節點指向棧頂,其他節點指向下一個節點
}LinkList, LinkNode;



bool initLink(LinkList*& list);	// 初始化雙向鏈表的棧
bool insertNode(LinkList*& list, LinkNode*& node); // 尾插法入棧
bool popNode(LinkList*& list, DateType& value);		// 出棧
bool gainNode(LinkList*& list, DateType& value);	// 獲取棧頂的值
bool clearLink(LinkList*& list);	// 釋放內存
void print_1(LinkList*& list);		// 單純輸出



bool initLink(LinkList*& list) {
	list = new LinkList;	// 分配內存
	if (!list) {
		cout << "內存分配失敗!" << endl;
		return false;
	}

	list->date = -1;
	list->size = 0;
	list->next = NULL;

	return true;
}


bool insertNode(LinkList*& list, LinkNode*& node) {
	if (!list || !node) {
		cout << "節點不存在!" << endl;
		return false;
	}

	// 實現連接
	node->next = list->next;	// 新節點的next指向頭節點的下一個節點
	list->next = node;			// 頭節點的next指向新節點

	list->size++;	// 棧的元素個數加一

	return true;
}



bool popNode(LinkList*& list, DateType& value) {
	if (!list) {
		cout << "節點不存在!" << endl;
		return false;
	}

	value = list->next->date;	// 將頭節點的下一個節點的值賦值給value返回

	list->size--;	// 棧的元素個數減一

	LinkNode* p = list->next;	// 將頭節點的下一個節點賦值給節點p
	list->next = p->next;		// 頭節點的next指向p的next
	delete p;	// 釋放p的內存

	return true;
}



bool gainNode(LinkList*& list, DateType& value) {
	if (!list) {
		cout << "節點不存在!" << endl;
		return false;
	}

	value = list->next->date;	// 將頭節點的下一個節點的值賦值給value返回

	return true;
}

bool clearLink(LinkList*& list) {
	if (!list) {
		return false;
	}

	list->size = 0;		// 棧的元素個數賦值0

	LinkList* p = list;		// 頭節點賦值給p
	while (p) {			// p不爲NULL執行循環
		list = list->next;	// 頭節點只想自己的下一個節點
		delete p;			// 釋放原頭節點的內存
		p = list;			// 頭節點賦值給p
	}
	cout << "釋放成功!" << endl;
	return true;
}

// 打印棧中的元素
void print_1(LinkList*& list) {
	if (!list) {
		cout << "節點不存在!" << endl;
		return;
	}

	LinkList* p = list->next;
	while (p) {
		cout << p->date << ", ";
		p = p->next;
	}

	cout << endl;
}


int main(void) {
	LinkList* list = NULL;
	LinkNode* node = NULL;

	// 初始化
	initLink(list);


	// 插入元素
	int n = 0;
	int value = 0;
	cout << "請輸入入棧的個數:";
	cin >> n;

	while (n > 0) {
		cin >> value;
		node = new LinkNode;
		node->date = value;

		insertNode(list, node);
		n--;
	}

	// 輸出
	cout << endl << endl << "插入元素後:" << endl;
	print_1(list);
	cout << "棧的元素個數是:" << list->size << endl;


	// 出棧
	popNode(list, value);
	cout << endl << endl << "棧頂出棧後,出棧元素是:" << value << endl;
	cout << "棧的元素個數是:" << list->size << endl;

	cout << endl << endl << "棧的元素:" << endl;
	print_1(list);


	// 獲取棧頂的元素
	gainNode(list, value);
	cout << endl << endl << "獲取棧頂的元素是:" << value << endl;

	// 釋放內存
	cout << endl << endl << "釋放棧的內存:";
	clearLink(list);

	print_1(list);

	system("pause");
	return 0;
}

運行截圖:
在這裏插入圖片描述


總結:
其實棧算是算法當中最簡單的了,他不像堆、鏈表順序表等那些那麼繁雜!
重點理解他是如何入棧和出棧就可以了!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章