七、【栈和队列】栈
棧 Stack
在第一節中我們提到,數據結構的邏輯結構分為線性結構和非線性結構,其中線性結構又可分為一般線性結構、受限線性結構和推廣結構。棧和隊列從數據結構上看和線性表很類似,但是他們在普通線性表的基礎上有額外的要求限制,因此說他們是受限的線性結構。這一節我們主要來了解什么是棧。
1 棧的基本概念
1.1 棧的定義
棧是只允許在表尾進行插入或刪除操作的線性表。從定義上來看,棧依然屬于線性表,只是它的操作僅限在表的一端執行。允許變化的一端通常稱為棧頂,一般用表尾構成;而始終固定的一端被稱為棧底,由表頭構成。
由于棧只能在表尾端進行插入或刪除操作,因此,最后插入的元素位于棧頂,在刪除時會被第一個彈出棧表,就像一疊盤子,最后放置的盤子位于最頂端,也會被最先拿走。例如上圖中的元素3是最后一個入棧的,也是第一個出棧的,棧的這種特性被稱為“后入先出”(Last In First Out,LIFO)。
1.2 棧的基本操作
由于棧只有表尾一端可以操作,所以棧的基本操作大部分都是圍繞表尾進行的。棧的主要操作如下:
| InitStack(&S) | 初始化一個空棧 S。 |
| DestroyStack(&S) | 銷毀棧,并釋放棧 S 占用的存儲空間。 |
| ClearStack(&S) | 將 S 清空為空棧。 |
| StackEmpty(S) | 判斷棧 S 是否為空,若為空則返回 true,否則返回 false。 |
| StackLength(S) | 返回棧 S 的元素個數,即棧的長度。 |
| GetTop(S, &x) | 讀取棧頂元素,若棧非空,則用 x 返回棧頂元素。 |
| Push(&S, x) | 入棧,若棧 S 未滿,則將 x 插入,使之成為新棧頂。 |
| Pop(&S, &x) | 出棧,若棧 S 非空,則彈出棧頂元素,并用 x 返回。 |
2 棧的順序表示和實現
棧是一種線性表,屬于邏輯結構。同其他的線性表一樣,棧也有兩種不同的存儲方式:順序存儲和鏈式存儲。
2.1 順序棧
順序棧是采用順序存儲的棧,利用一組地址連續的存儲單元來存放自棧頂到棧底的數據元素,同時設置一個指針指向棧頂元素。
靜態順序棧使用靜態數組來實現(如上圖所示),使用一個整型變量 top 來記錄棧頂元素的位置,即元素在數組的下標。由于數組的第一位元素下標為0,因此一般將 top 指針指向 -1 來表示空棧。當有元素時, top 指針指向棧頂元素。
動態順序棧使用動態數組來實現(如上圖所示),和靜態順序棧最大的區別就是棧頂指針的表示不同。動態順序棧一般有兩個指針 base 和 top,一個指向棧底,一個指向棧頂。當棧表為空時,base 和 top 指向同一個位置;當棧表不為空時,top 始終指向棧頂元素的下一位(邏輯上的下一位,在圖上表示則為棧頂元素的上方)。此時,棧的有效長度可以用 top-base 來獲取。
2.2 順序棧的實現
2.2.1 順序棧類型定義
/********** 動態順序棧的類型定義 **********/ #define INITSIZE 10 #define INCREMENT 10typedef int ElemType; typedef struct {ElemType *base; // 棧底指針 ElemType *top; // 棧頂指針int capacity; // 棧當前的最大容量 } SqStack;2.2.2 主要操作的實現
棧的主要操作都是圍繞表尾進行的,所以重點是處理棧頂指針 top 。詳情見附錄。
2.3 共享棧
共享棧是一種特殊的順序棧,利用棧底位置相對不變的特性,可以讓兩個順序棧共享一個一維數組,將兩個棧的棧底分別設置在共享空間的兩端,兩個棧頂向共享空間的中部延伸,如下圖所示:
和靜態順序棧一樣,如果棧表不為空,棧頂指針指向棧頂元素;如果棧表為空,他們的棧頂指針分別等于 -1 和 MaxSize。當插入新元素時,都需要先移動棧頂指針,再賦值。當兩個棧頂指針相鄰時(B.top - A.top = 1),即代表共享棧已滿。
共享棧的優點是可以更好地利用存儲空間,只有在整個存儲空間都被占滿時才發生上溢(超出最大范圍)。
2.3.1 共享棧類型定義
/********** 共享棧類型定義 **********/ #define MAXSIZE 10 typedef int ElemType; typedef struct {ElemType data[MAXSIZE];int top1; int top2; } SharedStack;2.3.2 主要操作的實現
共享棧的插入和刪除操作需要先選擇待操作的棧是哪一個,再開始執行操作。其余部分和順序棧的操作一致,詳情見附錄。
3 棧的鏈式表示和實現
采用鏈式存儲的棧稱為鏈棧。鏈棧的優點是便于多個棧共享存儲空間,且不存在棧滿上溢的情況。
由于棧的操作僅在表的一端完成,正好符合單鏈表可以在表頭快速執行插入和刪除操作的特點,所以一般將棧頂指針指向單鏈表的表頭。此時,鏈棧就是一個只能在表頭進行操作的單鏈表。
鏈棧的實現基本和單鏈表一致,所以這里不再贅述。實現詳情見附錄。
相關章節
第一節 【緒論】數據結構的基本概念
第二節 【緒論】算法和算法評價
第三節 【線性表】線性表概述
第四節 【線性表】線性表的順序表示和實現
第五節 【線性表】線性表的鏈式表示和實現
第六節 【線性表】雙向鏈表、循環鏈表和靜態鏈表
第七節 【棧和隊列】棧
第八節 【棧和隊列】棧的應用
第九節 【棧和隊列】棧和遞歸
第十節 【棧和隊列】隊列
附錄
順序棧的實現
靜態順序棧的實現
/** File name: StaticSqStack.h* -----------------------* 靜態順序棧的實現。*/#ifndef _STATIC_SEQUENTIAL_STACK_h_ #define _STATIC_SEQUENTIAL_STACK_h_#include <iostream> #include <stdio.h> #include <stdlib.h> using namespace std;/********** 靜態順序棧的類型定義 **********/ #define MAXSIZE 10 typedef int Elemtype; typedef struct {Elemtype data[MAXSIZE];int top; // 棧頂指針,指向-1表示空棧 } SqStack;/********** 靜態順序棧的實現 **********/ /** Function: 初始化操作* ----------------------------* 初始化一個空棧S*/ void InitStack(SqStack &S){S.top = -1; // 指針歸零 }/** Function: 銷毀棧操作* ----------------------------* 靜態數組的銷毀不需要手動執行,因此無需銷毀操作。*/ void DestoryStack(SqStack &S){}/** Function: 清空棧操作* ----------------------------* 將S清為空棧。*/ void ClearStack(SqStack &S){S.top = -1; // 指針歸零 }/** Function: 判空操作* ----------------------------* 判斷棧S是否為空,若為空則返回true,否則返回false。*/ bool StackEmpty(SqStack S){return S.top==-1; }/** Function: 求棧長操作* ----------------------------* 返回棧S的元素個數,即棧的長度。*/ int StackLength(SqStack S){return S.top+1; }/** Function: 讀取棧頂元素操作* ----------------------------* 讀取棧頂元素,若棧非空,則用e返回棧頂元素。*/ bool GetTop(SqStack S, Elemtype &e){if (StackEmpty(S)){return false;}e = S.data[S.top];return true; }/** Function: 入棧操作* ----------------------------* 若棧S未滿,則將e插入,使之成為新棧頂。*/ bool Push(SqStack &S, Elemtype e){if (StackLength(S)<MAXSIZE){ // 如果還有空間,則繼續插入操作S.data[++S.top] = e;return true;} else {printf("Out of space!\n"); return false;} }/** Function: 出棧操作* ----------------------------* 若棧S非空,則彈出棧頂元素,并用e返回。*/ bool Pop(SqStack &S, Elemtype &e){if (StackEmpty(S)){return false;} else {e = S.data[S.top--];return true;} }/** Function: 輸出操作* ----------------------------* 按從表頭到表尾的順序輸出*/ void Print(SqStack S){for (int i=0;i<=S.top;i++){printf("%d ", S.data[i]);}printf("\n"); }#endif // _STATIC_SEQUENTIAL_STACK_h_動態順序棧的實現
/** File name: DynamicSqStack.h* -----------------------* 動態順序棧的實現*/#ifndef _DYNAMIC_SEQUENTIAL_STACK_h_ #define _DYNAMIC_SEQUENTIAL_STACK_h_#include <iostream> #include <stdio.h> #include <stdlib.h> using namespace std;/********** 動態順序棧的類型定義 **********/ #define INITSIZE 10 #define INCREMENT 10typedef int ElemType; typedef struct {ElemType *base; // 棧底指針 ElemType *top; // 棧頂指針int capacity; // 棧當前的最大容量 } SqStack;/********** 主要操作的實現 **********/ /** Function: 初始化操作* ----------------------------* 初始化一個空棧S*/ void InitStack(SqStack &S){S.capacity = INITSIZE; // 設定初始最大容量S.base = new ElemType[S.capacity]; // 創建空棧表S.top = S.base; // 將棧頂指針歸零 }/** Function: 銷毀棧操作* ----------------------------* 銷毀棧,并釋放棧S占用的存儲空間。*/ void DestoryStack(SqStack &S){S.top=S.base;delete[] S.base;S.capacity = 0; }/** Function: 清空棧操作* ----------------------------* 將S清為空棧。*/ void ClearStack(SqStack &S){S.top = S.base; }/** Function: 判空操作* ----------------------------* 判斷棧S是否為空,若為空則返回true,否則返回false。*/ bool StackEmpty(SqStack S){return S.top==S.base; }/** Function: 求棧長操作* ----------------------------* 返回棧S的元素個數,即棧的長度。*/ int StackLength(SqStack S){return S.top-S.base; }/** Function: 讀取棧頂元素操作* ----------------------------* 讀取棧頂元素,若棧非空,則用e返回棧頂元素。*/ bool GetTop(SqStack S, ElemType &e){if (StackEmpty(S)){return false;}e = *(S.top-1); // 指針向前移動一位,指向棧頂元素return true; }/** Function: 入棧操作* ----------------------------* 若棧S未滿,則將e插入,使之成為新棧頂。*/ bool Push(SqStack &S, ElemType e){if (StackLength(S)>=S.capacity){ // 如果沒有空間,則首先進行擴容操作// 重新分配base指向內存的大小S.base = (ElemType *) realloc (S.base, (S.capacity+INCREMENT) * sizeof(ElemType));S.top = S.base + S.capacity;S.capacity += INCREMENT; }*S.top++ = e; // ++優先級高于*,所以可分解為:*S.top = e; S.top = S.top+1; return true; }/** Function: 出棧操作* ----------------------------* 若棧S非空,則彈出棧頂元素,并用e返回。*/ bool Pop(SqStack &S, ElemType &e){if (StackEmpty(S)){return false;} else {e = *--S.top;return true;} }/** Function: 輸出操作* ----------------------------* 按從表頭到表尾的順序輸出*/ void Print(SqStack S){for (int i=0;i<StackLength(S);i++){printf("%d ", S.base[i]);}printf("\n"); }#endif // _DYNAMIC_SEQUENTIAL_STACK_h_順序棧檢測程序
/** File name: SenquentialStackTest.cpp* -----------------------* 檢測靜態順序棧和動態順序棧*/// 引用靜態順序棧 // #include "StaticSqStack.h" // 引用動態順序棧 #include "DynamicSqStack.h"int main(){SqStack S;InitStack(S);int n;ElemType e;char helpInfo[] ="*****************************\n""Sequential Stack check: \n""\t-2-Quit\n""\t1-Push\n""\t2-Pop\n""\t3-Empty check\n""\t4-Get Length\n""\t5-Get top\n""\t6-Clear\n""\t7-Print\n""*****************************\n";while (n!=-2){printf(helpInfo);scanf("%d", &n);switch(n){case 1:printf("Enter the new value: ");scanf("%d", &e);Push(S, e);break;case 2:if (Pop(S, e)){printf("The last value %d is popped.\n", e);} else {printf("The Stack is empty.\n");}break;case 3:if (StackEmpty(S)){printf("The Stack is empty.\n");} else {printf("The Stack is not empty.\n");}break;case 4:printf("The length of Stack is: %d\n", StackLength(S));break;case 5:if (GetTop(S, e)){printf("The top value is: %d\n", e);} else {printf("The Stack is empty.\n");}break;case 6:ClearStack(S);printf("All cleared.\n");break;case 7:printf("Stack is: ");Print(S);break;}}DestoryStack(S);return 0; }共享棧的實現
共享棧的實現
/** Filename: SharedStack.h* -----------------------* 實現共享棧。*/#ifndef _SHARED_STACK_h_ #define _SHARED_STACK_h_#include <iostream> #include <stdio.h> #include <stdlib.h> using namespace std;/********** 共享棧類型定義 **********/ #define MAXSIZE 10 typedef int ElemType; typedef struct {ElemType data[MAXSIZE];int top1; int top2; } SharedStack;/********** 共享棧的實現 **********/ /** Function: 初始化操作* ----------------------------* 初始化一個空的共享棧S*/ void InitStack(SharedStack &S){S.top1 = -1; // 指針歸零S.top2 = MAXSIZE; }/** Function: 清空棧操作* ----------------------------* 將S清為空棧。*/ void ClearStack(SharedStack &S){InitStack(S); // 指針歸零 }/** Function: 判滿操作* ----------------------------* 判斷棧S是否已滿,若已滿則返回true,否則返回false。*/ bool StackFull(SharedStack S){return S.top2-S.top1==1; }/** Function: 求棧長操作* ----------------------------* 返回棧S的元素個數,即總的棧長。*/ int StackLength(SharedStack S){return (S.top1+1 + MAXSIZE-S.top2); }/** Function: 入棧操作* ----------------------------* 若棧S未滿,則選擇一個鏈表將e插入,使之成為新棧頂。* 使用變量n(0或1)來指示選擇插入的棧。*/ bool Push(SharedStack &S, ElemType e, int n){if (!StackFull(S)){ // 如果還有空間,則繼續插入操作if (n){ // 如果n為1,插入反向增長的棧S.data[--S.top2] = e;} else {S.data[++S.top1] = e;}return true;} else {printf("Out of space!\n"); return false;} }/** Function: 出棧操作* ----------------------------* 若棧S非空,則選擇一個棧彈出棧頂元素,并用e返回。* 使用變量n(0或1)來指示選擇插入的棧。*/ bool Pop(SharedStack &S, ElemType &e, int n){if (n){ // 如果n為1,選擇反向增長的棧if (S.top2==MAXSIZE){ // 如果該棧為空,返回falsereturn false;} else {e = S.data[S.top2++];return true;}} else { // 如果n為0,選擇正向增長的棧if (S.top1==-1){return false;} else {e = S.data[S.top1--];return true;}} }/** Function: 輸出操作* ----------------------------* 將兩個表的元素分別按棧內的順序輸出。*/ void Print(SharedStack S){for (int i=0;i<=S.top1;i++){printf("%d ->", S.data[i]);}printf("\n");for (int j=MAXSIZE-1;j>=S.top2;j--){printf("%d ->", S.data[j]);}printf("\n"); }#endif // _SHARED_STACK_h_共享棧檢測程序
/** Filename: SharedStack.cpp* -----------------------* 檢測共享棧的各個方法。*/#include "SharedStack.h"int main(){SharedStack S;InitStack(S);int n;ElemType e;char helpInfo[] ="*****************************\n""Sequential Stack check: \n""\t-2-Quit\n""\t1-Push\n""\t2-Pop\n""\t3-Full check\n""\t4-Get Length\n""\t5-Clear\n""\t6-Print\n""*****************************\n";while (n!=-2){printf(helpInfo);scanf("%d", &n);switch(n){case 1:printf("Enter the new value: ");scanf("%d", &e);printf("Choose a stack<0/1>: ");scanf("%d", &n);Push(S, e, n);break;case 2:printf("Choose a stack<0/1>: ");scanf("%d", &n);if (Pop(S, e, n)){printf("The last value %d is popped.\n", e);} else {printf("The Stack is empty.\n");}break;case 3:if (StackFull(S)){printf("The Stack is full.\n");} else {printf("The Stack is not full.\n");}break;case 4:printf("The length of Stack is: %d\n", StackLength(S));break;case 5:ClearStack(S);printf("All cleared.\n");break;case 6:printf("Stack is: \n");Print(S);break;}}return 0; }鏈棧的實現
鏈棧的實現
/** Filename: LinkedStack.h* -----------------------* 使用單鏈表來實現鏈棧*/#ifndef _SINGLE_LINKED_LIST_h_ #define _SINGLE_LINKED_LIST_h_#include <iostream> #include <stdio.h> #include <stdlib.h> using namespace std;/********** 鏈棧的結點的類型定義 **********/ typedef int ElemType; typedef struct LNode{ElemType data; // 數據域struct LNode *next; // 指針域 } LNode, *LinkedStack;/********** 主要操作的實現 **********/ /** Function: 初始化操作* ----------------------------* 初始化一個空棧*/ void InitStack(LinkedStack &S){S = new LNode;S->next = NULL; }/** Function: 清空棧操作* ----------------------------* 將S清為空棧。*/ void ClearStack(LinkedStack &S){LinkedStack q; // 指向待刪除結點while (S->next!=NULL){ // 不斷刪除第一個結點q = S->next;S->next = q->next;delete q;} }/** Function: 銷毀棧操作* ----------------------------* 銷毀棧,并釋放棧 S 占用的存儲空間。*/ void DestroyStack(LinkedStack &S){ClearStack(S);delete S; }/** Function: 判空操作* ----------------------------* 判斷棧S是否為空,若為空則返回true,否則返回false。*/ bool StackEmpty(LinkedStack S){return !S->next; }/** Function: 求棧長操作* ----------------------------* 返回棧S的元素個數,即棧的長度。*/ int StackLength(LinkedStack S){int count=0;LinkedStack tmp=S;while (tmp->next!=NULL){tmp = tmp->next;count++;}return count; }/** Function: 讀取棧頂元素操作* ----------------------------* 讀取棧頂元素,若棧非空,則用e返回棧頂元素。*/ bool GetTop(LinkedStack S, ElemType &e){if (StackEmpty(S)){return false;}e = S->next->data;return true; }/** Function: 入棧操作* ----------------------------* 使用頭插法將e插入,使之成為新棧頂。*/ void Push(LinkedStack &S, ElemType e){LNode *n = new LNode;n->data = e;n->next = S->next;S->next = n; }/** Function: 出棧操作* ----------------------------* 若棧S非空,則彈出棧頂元素,并用e返回。*/ bool Pop(LinkedStack &S, ElemType &e){LinkedStack q;if (StackEmpty(S)){return false;} else {q = S->next;e = q->data;S->next = q->next;delete q;return true;} }/** Function: 輸出操作* ----------------------------* 由于單鏈表的特性,更方便按從棧尾到棧頭的順序輸出。* 后續可以用遞歸法來正向輸出。*/ void Print(LinkedStack S){LinkedStack tmp=S;while (tmp->next!=NULL){tmp = tmp->next;printf("%d <- ", tmp->data);}printf("base\n"); }#endif // _SINGLE_LINKED_LIST_h_鏈棧的檢測程序
/** Filename: LinkedStackTest.cpp* -----------------------* 用來檢測鏈棧各方法。*/#include "LinkedStack.h"int main(){LinkedStack S;InitStack(S);int n;ElemType e;char helpInfo[] ="*****************************\n""Sequential Stack check: \n""\t-2-Quit\n""\t1-Push\n""\t2-Pop\n""\t3-Empty check\n""\t4-Get Length\n""\t5-Get top\n""\t6-Clear\n""\t7-Print\n""*****************************\n";while (n!=-2){printf(helpInfo);scanf("%d", &n);switch(n){case 1:printf("Enter the new value: ");scanf("%d", &e);Push(S, e);break;case 2:if (Pop(S, e)){printf("The last value %d is popped.\n", e);} else {printf("The Stack is empty.\n");}break;case 3:if (StackEmpty(S)){printf("The Stack is empty.\n");} else {printf("The Stack is not empty.\n");}break;case 4:printf("The length of Stack is: %d\n", StackLength(S));break;case 5:if (GetTop(S, e)){printf("The top value is: %d\n", e);} else {printf("The Stack is empty.\n");}break;case 6:ClearStack(S);printf("All cleared.\n");break;case 7:printf("Stack is: ");Print(S);break;}}DestoryStack(S);return 0;}總結
- 上一篇: 五、【线性表】线性表的链式表示和实现
- 下一篇: 十、【栈和队列】队列