一、Stack簡介
stack 是容器適配器的一種。要使用 stack,必須包含頭文件 <stack>。
stack就是“棧”。棧是一種後進先出的元素序列,訪問和刪除都只能對棧頂的元素(即最後一個被加入棧的元素)進行,並且元素也只能被添加到棧頂。棧內的元素不能訪問。如果一定要訪問棧內的元素,只能將其上方的元素全部從棧中刪除,使之變成棧頂元素纔可以。
容器適配器中的數據是以 LIFO 的方式組織的,這和自助餐館中堆疊的盤子、箱子中的一堆書類似。下圖展示了一個理論上的 stack 容器及其一些基本操作。只能訪問 stack 頂部的元素;只有在移除 stack 頂部的元素後,才能訪問下方的元素。
思考:1、如果進站的車廂序列爲123,則可能的出站車廂序列是哪些? 會有312嗎?
2、如果進站的車廂序列爲123456,問能否得到135426和435612的出站序列?
1.stack對象的默認構造
stack採用模板類實現, stack對象的默認構造形式: stack <T> stkT;
stack <int> stkInt; //一個存放int的stack容器。
stack <float> stkFloat; //一個存放float的stack容器。
stack <string> stkString; //一個存放string的stack容器。
//尖括號內還可以設置指針類型或自定義類型。
二、成員函數詳解
1.stack的進棧與出棧方法:(push()與pop())
- stack.push(elem); //往棧頭添加元素
- stack.pop(); //從棧頭移除第一個元素
#include<bits/stdc++.h>
using namespace std;
void objPlay2()
{ stack<int> stkInt;
stkInt.push(1); //放進去1
stkInt.push(3); //放進去3
stkInt.pop(); //彈出來一個元素
stkInt.push(5); //放進去5
stkInt.push(7); //放進去7
stkInt.push(9); //放進去9 此時元素就是1,5,7,9
stkInt.pop(); //彈出來一個元素
stkInt.pop();//彈出來一個元素 此時元素就是1,5
}
int main()
{
objPlay2();
return 0;
}
2.stack對象的拷貝構造與賦值
- stack(const stack &stk); //拷貝構造函數
- stack& operator=(const stack &stk); //重載等號操作符
#include<bits/stdc++.h>
using namespace std;
int main()
{ stack<int> stkIntA;
stkIntA.push(1);
stkIntA.push(3);
stkIntA.push(5);
stkIntA.push(7);
stkIntA.push(9);
stack<int> stkIntB(stkIntA); //拷貝構造
stack<int> stkIntC;
stkIntC = stkIntA; //賦值
}
3.stack的數據存取
- stack.top(); //返回最後一個壓入棧元素
#include<bits/stdc++.h>
using namespace std;
int main()
{ stack<int> stkIntA;
stkIntA.push(1);
stkIntA.push(3);
stkIntA.push(5);
stkIntA.push(7);
stkIntA.push(9);
int iTop = stkIntA.top(); cout<<iTop; //獲取棧頂元素,那就是9,top只是獲取棧頂元素,pop是彈出棧頂元素
stkIntA.top() = 19; //19
iTop = stkIntA.top(); cout<<iTop;
}
4.stack的大小
- stack.empty(); //判斷堆棧是否爲空
- stack.size(); //返回堆棧的大小
#include<bits/stdc++.h>
using namespace std;
int main()
{ int iSize;
stack<int> stkIntA;
stkIntA.push(1);
stkIntA.push(3);
stkIntA.push(5);
stkIntA.push(7);
stkIntA.push(9);
if (!stkIntA.empty())
iSize = stkIntA.size();//5個元素
cout<<iSize;
}
三、stack的應用
1、表達式括號匹配(stack)
【問題描述】
假設一個表達式有英文字母(小寫)、運算符(+,—,*,/)和左右小(圓)括號構成,以“@”作爲表達式的結束符。請編寫一個程序檢查表達式中的左右圓括號是否匹配,若匹配,則返回“YES”;否則返回“NO”。表達式長度小於 255,左圓括號少於 20 個。
【輸入文件】
輸入文件 stack.in 包括一行數據,即表達式,
【輸出文件】
輸出文件 stack.out 包括一行,即“YES” 或“NO”。
【輸入輸出樣例】
【樣例輸入 1 】
2*(x+y)/(1-x)@
【樣例輸出 1 】
YES
【樣例輸入 2 】
(25+x)*(a*(a+b+b)@
【樣例輸出 2 】
NO
2、字符串匹配。
字符串中只含有(),{},[],< >,判斷輸入的字符串中括號是否匹配。如果括號有互相包含的形式,從內到外必須是 < > ,( ),[ ],{ },例如:輸入[()],輸出YES,而輸入([ ])、([))都應該輸出NO。
輸入格式:
第一行1個整數n,表示以下有多少個由括號組成的字符串。
接下來的n行,每行都是一個由括號組成的長度不超過255的字符串。
輸出格式:
輸出n行,每行都是一個字符串“YES”或“NO”。
【輸入樣例】
5
{}{}<><>()()[][]
{{}}{{}}<<>><<>>(())(())[[]][[]]
{{}}{{}}<<>><<>>(())(())[[]][[]]
{<>}{[]}<<<>><<>>>((<>))(())[[(<>)]][[]]
><}{{[]}<<<>><<>>>((<>))(())[[(<>)]][[]]
【輸出標例】
YES
YES
YES
YES
NO
3、表達式求值
【題目描述】
給定一個只包含加法和乘法的算術表達式,請編程計算表達式的值。
【輸入格式】
輸入僅有一行,爲計算所需要的表達式,表達式中只包含數字、加法運算符“+”和乘法運算符“*”,且沒有括號,所有參與運算的數字均爲 0~231 -1 之間的整數。
輸入數據保證這一行只有 0~9、+、* 這 12 種字符。
【輸出格式】
輸出只有一行,包含一個整數,表示這個表達式的值。
注意:當答案長度多於 4 位時,請只輸出最後 4 位,前導 0 不輸出。
【輸入輸出樣例】
【樣例解釋】
樣例 1 計算的結果爲 8,直接輸出 8。
樣例 2 計算的結果爲 1234567891,輸出後 4 位,即 7891。
樣例 3 計算的結果爲 1000000004,輸出後 4 位,即 4。
【數據規模】
對於 30% 的數據,0≤表達式中加法運算符和乘法運算符的總數≤100。
對於 80% 的數據,0≤表達式中加法運算符和乘法運算符的總數≤1000。
對於 100% 的數據,0≤表達式中加法運算符和乘法運算符的總數≤100000。
4、編程求一個後綴表達式的值
【問題描述】
從鍵盤讀入一個後綴表達式(字符串),只含有0-9組成的運算數及加(+)、減(—)、乘(*)、除(/)四種運算符。每個運算數之間用一個空格隔開,不需要判斷給你的表達式是否合法。以@作爲結束標誌。
【算法分析】
後綴表達式的處理過程很簡單,過程如下:掃描後綴表達式,凡遇操作數則將之壓進堆棧,遇運算符則從堆棧中彈出兩個操作數進行該運算,將運算結果壓棧,然後繼續掃描,直到後綴表達式被掃描完畢爲止,此時棧底元素即爲該後綴表達式的值。
比如,16–9*(4+3)轉換成後綴表達式爲: 16 9 4 3 +*–,在字符數組A中的形式爲:
棧中的變化情況:
思考:A+B*(C-D)-E/F的後綴表達式是什麼?
5、排隊。
n個人排成一條直線(一排),給出隊伍中每個人的身高,每個人只能看到站在他右邊且個頭比他小的人。請求出所有人可以看到的人數之和。
輸入格式:
第1行1個正整數N,1<=N<=80000。
下面的N行,每行給出一個正整數h!,表示第i個人的身高。1<=hi<=10的9次方。
輸出格式:
一行一個數,表示所有人可以看到的人數之和。
輸入樣例:
6
10
3
7
4
12
2
輸出樣例:
5
6、車廂調度(train)
【問題描述】
有一個火車站,鐵路如圖所示,每輛火車從A 駛入,再從 B 方向駛出,同時它的車廂可以重新組合。假設從 A 方向駛來的火車有 n 節(n<=1000),分別按照順序編號爲 1,2,3,…,n。假定在進入車站前,每節車廂之間都不是連着的,並且它們可以自行移動到 B 處的鐵軌上。另外假定車站 C 可以停放任意多節車廂。但是一旦進入車站 C,它就不能再回到 A 方向的鐵軌上了,並且一旦當它進入 B 方向的鐵軌,它就不能再回到車站 C。負責車廂調度的工作人員需要知道能否使它以 a1,a2,…,an 的順序從 B 方向駛出,請來判斷能否得到指定的車廂順序。.
【輸入】
輸入文件的第一行爲一個整數 n,其中 n<=1000,表示有 n 節車廂,第二行爲 n 個數字,表示指定的車廂順序。
【輸出】
如果可以得到指定的車廂順序,則輸出一個字符串”YES”,否則輸出”NO”(注意要大寫,不包含引號)。
【輸入樣例】
5
5 4 3 2 1
【輸出樣例】
YES
7、溶液模擬器
【問題分析】
小 Y 雖有很多溶液,但還是沒有辦法配成想要的溶液,因爲萬一倒錯了就沒有辦法挽回了。他從網上下載了一個溶液配置模擬器:模擬器在計算機中構造一種虛擬溶液,然後可以虛擬地向當前虛擬溶液中加入一定濃度、一定質量的這種溶液,模擬器會快速地算出倒入後虛擬溶液的濃度和質量。
模擬器的使用步驟如下:
(1)爲模擬器設置一個初始質量和濃度 V0 、C0 % (0≤C0 ≤100)。
(2)進行一系列操作,模擬器支持兩種操作:一種是 P(v,c)操作,表示向當前的虛擬溶液中加入質量爲 v、濃度爲 c 的溶液;另一種是 Z 操作,即撤銷上一步 P 操作。
【輸入格式】
第 1 行兩個整數 V0 、C0 。
第 2 行 1 個整數 n,n≤10000,表示操作數。
接下來的 n 行,每行一條操作,格式爲:P_v_c 或 Z。其中“_”代表一個空格,當只剩初始溶液的時候,再撤銷就沒有用了。
任意時刻質量都不會超過 231 -1。
【輸出格式】
輸出 n 行,每行兩個數 Vi 、Ci ,之間用一個空格隔開,其中 Vi 爲整數,Ci 爲實數(保留 5 位小數)。其中,第 i 行表示第 i 次操作以後的溶液質量和濃度。
【輸入樣例】
100 100
2
P 100 0
Z
【輸出樣例】
200 50.000 00
100 100.000 00
8、火車進站出站問題。
給定一個正整數N代表火車數量,0<N<10,接下來輸入火車入站的序列,一共N輛火車,每輛火車以數字1-9編號。要求輸出火車出站的序列號。
解題思路:棧具有先進後出、後進先出的特點,因此,任何一個調度結果應該是1 ,2 ,3 ,4全排列中的一個元素。由於進棧的順序是由小到大的,所以出棧序列應該滿足以下條件:對於序列中的任何一個數其後面所有比它小的數應該是由大到小的,例如4321 是一個有效的出棧序列,1423不是一個有效的出棧結果(4 後面比它小的兩個數 2 ,3 不是倒序)。據此,本題可以通過算法產生n 個數的全排列,然後將滿足出棧規則的序列輸出。
#include<bits/stdc++.h>
using namespace std;
bool Pop(vector<int> pushV,vector<int> popV) {
stack<int> s;
int j=0;
for(int i=0;i<pushV.size()&&j<popV.size();i++)
{ if(pushV[i]!=popV[j])
{ if(!s.empty())
{ if(s.top()!=popV[j])
s.push(pushV[i]);
else
s.pop();
}
else
s.push(pushV[i]);
}
else j++;
}
while(!s.empty())
{ if(s.top()==popV[j++])
s.pop();
else
return false;
}
return true;
}
int main()
{ int n;
cin>>n;
vector<int> v(n);
for(int i=0;i<n;i++)
cin>>v[i];
vector<int> vv=v;
do{
if(Pop(vv,v))
{ for(int i=0;i<n;i++)
{ if(i==n-1)
cout<<v[i]<<endl;
else
cout<<v[i]<<" ";
}
}
}while(next_permutation(v.begin(),v.end()));
return 0;
}
依此遞歸定義,遞歸算法如下:
#include<bits/stdc++.h>
using namespace std;
int cont=1;
void print(int str[],int n);
void perm(int str[],int k,int n)
{ int i,temp;
if(k==n-1)print(str,n);//k和n-1相等,即一趟遞歸走完
else
{ for(i=k;i<n;i++)//把當前節點元素與後續節點元素交換
{ temp=str[k]; str[k]=str[i]; str[i]=temp;//交換
perm(str,k+1,n);//把下一個節點元素與後續節點元素交換
temp=str[i]; str[i]=str[k]; str[k]=temp;//恢復原狀
}
}
}
/* 本函數判斷整數序列 str[] 是否滿足進出棧規則, 若滿足則輸出*/
void print(int str[],int n)
{ int i,j,k,l,m,flag=1,b[2];
for(i=0;i<n;i++) /* 對每個str[i] 判斷其後比它小的數是否爲降序序列*/
{ m=0;
for(j=i+1;j<n&&flag;j++)
{ if (str[i]>str[j])
{ if (m==0) b[m++]=str[j];//記錄str[i]後比它小的數
else //如果之後出現的數比記錄的數還大,改變標記變量
{ if (str[j]>b[0]) flag=0;
//否則記錄這個更小的數
else b[0]=str[j];
}
}
}
}
if(flag) /* 滿足出棧規則則輸出 str[] 中的序列*/
{ printf(" %2d:",cont++); //輸出序號
for(i=0;i<n;i++)
printf("%d",str[i]);//輸出序列
printf("\n");
}
}
int main()
{ int str[100],n,i;
printf("input a int:"); /* 輸出排列的元素個數*/
scanf("%d",&n);
for(i=0;i<n;i++) /* 初始化排列集合*/
str[i]=i+1; //第i個節點賦值爲i+1
printf("input the result:\n");
perm(str,0,n); //調用遞歸
printf("\n");
return 0;
}
9、中綴表達式值(expr)
【問題描述】
輸入一箇中綴表達式(由 0-9 組成的運算數、加+減—乘*除/四種運算符、左右小括號組成。注意“—”也可作爲負數的標誌,表達式以“@”作爲結束符),判斷表達式是否合法,如果不合法,請輸出“NO”;否則請把表達式轉換成後綴形式,再求出後綴表達式的值並輸出。
注意:必須用棧操作,不能直接輸出表達式的值。
【輸入文件】
輸入文件的第一行爲一個以@結束的字符串。
【輸出文件】
如果表達式不合法,請輸出“NO”,要求大寫。
如果表達式合法,請輸出計算結果。
【輸入樣例】
1+2×8―9
【輸出樣例】
8
10、計算(calc)
【問題描述】
小明在你的幫助下,破密了 Ferrari 設的密碼門,正要往前走,突然又出現了一個密碼門,門上有一個算式,其中只有“(”,“)”,“0-9”,“+”,“-”,“*”,“/”,“^”,求出的值就是密碼。小明數學學得不好,還需你幫他的忙。(“/”用整數除法)
【輸入】
輸入文件 calc.in 共 1 行,爲一個算式。
【輸出】
輸出文件 calc.out 共 1 行,就是密碼。
【輸入輸出樣例】
【輸入樣例】
1+(3+2)*(7^2+6*9)/(2)
【輸出樣例】
258
【限制】
100%的數據滿足:算式長度<=30 其中所有數據在 2 31 -1 的範圍內。