數據結構——棧
數據結構——棧
1、棧的定義
棧是一種特殊的線性表。
其特殊性在於限定插入和刪除數據元素的操作僅僅能在線性表的一端進行。
該位置是表的末端。叫做棧頂(top
)
圖像模擬
左邊的棧的示意圖 右邊為用鐵路調度站表示棧
2、棧的基本運算
構造空棧:InitStack(S)
判棧空:
StackEmpty(S)
判棧滿:
StackFull(S)
進棧:
Push(S,x)
可形象地理解為壓入,這時棧中會多一個元素
退棧:
Pop(S)
可形象地理解為彈出,彈出後棧中就無此元素了。
取棧頂元素:StackTop(S)
不同與彈出,僅僅是使用棧頂元素的值,該元素仍在棧頂不會改變
3、棧的存儲結構
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)鏈式存儲
棧的操作詩線性表操作的特例:
若是棧中元素的數目變化範圍較大或不清楚棧元素的數目,就應該考慮使用鏈式存儲結構。人們將用鏈式存儲結構表示的棧稱作"鏈棧"。鏈棧通經常使用一個無頭結點的單鏈表表示。
簡單應用:平衡符號
#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);
}
4、C++ STL—stack
1)stack 模板類的定義在<stack>頭文件裏。
2)stack 模板類須要兩個模板參數。一個是元素類型。一個容器類型,但僅僅有元素類型是必要的,在不指定容器類型時,默認的容器類型為deque。
3)定義stack 對象的演示樣例代碼例如以下:stack<int> s1; stack<string> s2;
4)stack 的基本操作有:
入棧,如例:s.push(x);
出棧,如例:s.pop();
註意,出棧操作僅僅是刪除棧頂元素,並不返回該元素。
訪問棧頂,如例: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
數據結構——棧