一、棧的定義
棧是一種特殊的線性表。html
其特殊性在於限定插入和刪除數據元素的操做僅僅能在線性表的一端進行。前端
該位置是表的末端。叫作棧頂(top
)
圖像模擬
ios
左邊的棧的示意圖 右邊爲用鐵路調度站表示棧
二、棧的基本運算
構造空棧:InitStack(S)
判棧空: StackEmpty(S)
判棧滿: StackFull(S)
進棧: Push(S,x)
可形象地理解爲壓入,這時棧中會多一個元素
退棧: Pop(S)
可形象地理解爲彈出,彈出後棧中就無此元素了。
取棧頂元素:StackTop(S)
不一樣與彈出,僅僅是使用棧頂元素的值,該元素仍在棧頂不會改變
三、棧的存儲結構
1)順序存儲
順序存儲也叫數組存儲。
潛在問題:
需要提早預計一個數組的大小。事實上在實際應用中,這個不難作到;對錯誤的檢測嚴重影響運行的效率問題(在順序棧中有"上溢"和"下溢"的概念。算法
順序棧比如一個盒子。咱們在裏頭放了一疊書。當咱們要用書的話僅僅能從第一本開始拿(你會把盒子翻過來嗎?真聰明^^),那麼當咱們把書本放到這個棧中超過盒子的頂部時就放不下了(疊上去的不算,哼哼),這時就是"上溢","上溢"也就是棧頂指針指出棧的外面。顯然是出錯了。數組
反之。當棧中已沒有書時,咱們再去拿,看看沒書,把盒子拎起來看看盒底,仍是沒有,這就是"下溢"。"下溢"自己可以表示棧爲空棧,所以可以用它來做爲控制轉移的條件。)
簡單應用:平衡符號
算法思想:作一個空棧。讀入字符直到文件結尾,假設字符是一個開放符號。將其入棧。假設是一個封閉符號,則當棧空時報錯,不然,將元素彈出,假設彈出的符號不是其相應的開放符號。則報錯。數據結構
到了文件尾,假設棧非空則報錯。函數
#include <iostream> #include <string> const int maxn = 100; using namespace std; struct *Stack { int Capacity;//棧的容量 int Top_of_stack;//棧的下標 char *Array; //存放棧中元素的數組 }; //棧的數組實現 typedef struct Mystack *Stack; Stack CreateStack(int Max);//建立一個棧 void DisposeStack(Stack S);//釋放棧 int IsEmpty(Stack S);//推斷一個棧是不是空棧 int IsFull(Stack S);//推斷一個棧是不是滿棧 void Push(int x, Stack S);//入棧 void Pop(Stack S);//出棧 char Top(Stack S);//獲取棧頂元素 Stack CreateStack(int Max) { Stack S; S = (Stack)malloc(sizeof(struct Mystack)); if(S == NULL) cout << "Create stack error!" << endl; S->Array = (char *)malloc(sizeof(char) * Max); if(S->Array == NULL) cout << "Create stack error!" << endl; S->Capacity = Max; S->Top_of_stack = 0; return S; } //釋放棧 void DisposeStack(Stack S) { if(S != NULL) { free(S -> Array); free(S); } } //推斷一個棧是不是空棧 int IsEmpty(Stack S) { return !S->Top_of_stack; } //推斷一個棧是否爲滿棧 int IsFull(Stack S) { if(S->Top_of_stack == S->Capacity - 1) return 1; else return 0; } //數據入棧 void Push(int x, Stack S) { if(IsFull(S)) cout << "The Stack is full!" << endl; else S->Array[S->Top_of_stack++] = x; } //數據出棧 void Pop(Stack S) { if(IsEmpty(S)) cout << "The Stack is empty!" << endl; else S->Top_of_stack--; } //將棧頂返回 char Top(Stack S) { if(!IsEmpty(S)) return S->Array[S->Top_of_stack - 1]; cout << "The Stack is empty!" << endl; return 0; } int main() { char str[maxn]; cin >> str; int len = strlen(str); //依據序列的長度來建立棧 struct Mystack *my_stack = CreateStack(len + 1); for(int i = 0;i < len; i ++) { //假設字符時開放符號,則將其推入棧中 if(str[i] == '{' || str[i] == '[' || str[i] == '(') Push(str[i],my_stack); //假設字符時一個封閉符號,則當棧空時報錯,不然將棧彈出來 if(str[i] == '}') { if(Top(my_stack) == '{') Pop(my_stack); else break; } else if(str[i] == ']') { if(Top(my_stack) == '[') Pop(my_stack); else break; } else if(str[i] == ')') { if(Top(my_stack) == '(') Pop(my_stack); else break; } } //假設最後佔空則序列是合法的,不然是不平衡的 if(IsEmpty(my_stack)) cout << "The symbol that you input is balance!" << endl; else cout << "The symbol that you inut is imbalance!" << endl; DisposeStack(my_stack); return 0; }
2)鏈式存儲
棧的操做詩線性表操做的特例:
如果棧中元素的數目變化範圍較大或不清楚棧元素的數目,就應該考慮使用鏈式存儲結構。人們將用鏈式存儲結構表示的棧稱做"鏈棧"。鏈棧通常常使用一個無頭結點的單鏈表表示。
簡單應用:平衡符號
post
#include <stdio.h>#include <stdlib.h>#define Error(Str) fprintf(stderr,"%s\n",Str),exit(1) struct Node{ char elem; struct Node *next; };//棧的鏈表實現 typedef struct Node *Stack; int CheckSymbol(Stack S);//檢測平衡符號的函數 Stack CreateStack(void);/*建立一個空棧*/void MakeEmpty(Stack); int IsEmpty(Stack);//測試棧是不是空棧void Push(char ,Stack);//入棧void Pop(Stack);//出棧char Top(Stack);//獲取棧頂元素void DisposeStack(Stack);//銷燬棧 int main() { Stack S; S=CreateStack(); if(CheckSymbol(S)) printf("wrong\n"); else printf("right\n"); DisposeStack(S); return 0; } int CheckSymbol(Stack S) { char ch; printf("input characters as {,} or (,)or [,] \n"); printf("and # to quit\n"); while((ch=getchar())!='#')//輸入平衡字符 { if(ch=='{'||ch=='['||ch=='(') /*開放符號*/ Push(ch,S); else if(ch=='}'||ch==']'||ch==')') /*封閉符號*/ { if(IsEmpty(S)) /*棧裏無字符*/ Error("stack is empty."); else { switch(ch) { case '}': if(Top(S)!='{')//不匹配 Error("not match"); else break;//匹配成功 case ')':if(Top(S)!='(') Error("not match"); else break; case ']':if(Top(S)!='[') Error("not match"); else break; } /*匹配成功,將棧中匹配成功的符號彈出*/ Pop(S); } } } if(!IsEmpty(S))//假設最後棧裏還有字符,則說明未匹配完,即出錯 Error("the stack is not empty last"); else return 0;//成功 } /*棧的基本操做——鏈表實現*/ Stack CreateStack(void) { Stack S; S=malloc(sizeof(struct Node)); if(S==NULL) Error("out of space"); S->next=NULL; MakeEmpty(S); return S; } void MakeEmpty(Stack S)//設置Next指針指向NULL { if(S==NULL) //未建立棧 Error("must usr CreateStack first"); else while(!IsEmpty) Pos(S); } int IsEmpty(Stack S) { return S->next==NULL; } void Push(char ch,Stack S)//向前鏈表前端進行插入實現 { Stack tmp; tmp=malloc(sizeof(struct Node)); if(!tmp) Error("out of space"); tmp->elem=ch; tmp->next=S->next; S->next=tmp; } void Pop(Stack S)//經過刪除表的前端元素而實現 { Stack tmp; if(IsEmpty(S)) Error("empty stack"); else { tmp=S->next; S->next=tmp->next; free(tmp); } } char Top(Stack S)//經過考查表的第一個位置上元素而完畢的 { if(!IsEmpty(S)) return S->next->elem; Error("empty stack."); return 0; } void DisposeStack(Stack S) { if(S==NULL) Error("no stack"); MakeEmpty(S); free(S); }
四、C++ STL—stack
1)stack 模板類的定義在<stack>頭文件裏。
2)stack 模板類需要兩個模板參數。一個是元素類型。一個容器類型,但僅僅有元素類型是必要的,在不指定容器類型時,默認的容器類型爲deque。ui
3)定義stack 對象的演示樣例代碼例如如下:stack<int> s1; stack<string> s2;
4)stack 的基本操做有:
入棧,如例:s.push(x);
出棧,如例:s.pop();
注意,出棧操做僅僅是刪除棧頂元素,並不返回該元素。spa
訪問棧頂,如例:s.top()
推斷棧空。如例:s.empty()
,當棧空時,返回true。
訪問棧中的元素個數,如例:s.size()
。
簡單應用:中綴到後綴的轉換
算法思路:
①遇到數字:直接輸出
②遇到'(':壓棧
③遇到')':持續出棧,假設出棧的符號不是'('則輸出。不然終止出棧。
④遇到符號:推斷該符號與棧頂符號的運算優先級,假設棧頂符號的運算優先級高。則出棧並輸出,直到優先級相等或棧爲空;假設棧頂符號的運算優先級低於或等於當前符號的運算優先級,則將當前符號壓棧。
⑤處理完字符串後將棧中剩餘的符號全部輸出。
例子:中綴表達式:6*[5+(2+3)*8+3]
後綴表達式:6523+8*+3+*
#include<iostream>#include<string>#include<sstream> //基於字符串的流#include<stack> //STL堆棧容器using namespace std; int main() { //輸入字符串 string in; cin >> in; stack<char> stack; for(size_t i = 0; i != in.size(); ++i) //size_t是在標準C庫中定義,爲unsigned int { if(in[i] == '(' || in[i] == '*' || in[i] == '/') //遇到'('入棧,由於*/優先級別比棧頂高,直接入棧 { stack.push(in[i]); } //持續出棧 else if(in[i] == ')') { while(1) { char tmp = stack.top(); stack.pop(); if(tmp != '(') { cout << tmp; } else break; } } else if(in[i] == '+' || in[i] == '-') { if(stack.top() == '*' || stack.top() == '/') { while(stack.top() == '*' || stack.top() == '/') { cout << stack.top(); stack.pop();//比棧頂優先級低。直接出棧並且輸出 } cout << in[i]; } else stack.push(in[i]); } else cout << in[i]; //遇到數字直接輸出 } while(stack.size()) { cout << stack.top(); stack.pop(); } }
不少其它應用:
UVa514 Rails(鐵軌)
UVa442 Matrix Chain Multiplication(矩陣鏈乘)
參考文獻與連接:
《數據結構與算法分析——C語言描寫敘述》
《算法競賽入門經典》
堆棧概念
C++ STL