棧的原理精講
棧是一種線性結構,即線性排列。
好比如一條衚衕:
先進來的車子只能等後面進來的車子出去後,他才能出去,也就體現了棧的特性:“後進先出”!
棧也是一種線性表,只不過它是操作受限的線性表,只能在一端操作。
進出的一端稱爲棧頂(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;
}
運行截圖:
總結:
其實棧算是算法當中最簡單的了,他不像堆、鏈表順序表等那些那麼繁雜!
重點理解他是如何入棧和出棧就可以了!