STL詳解(二) 棧容器Stack

一、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

輸出樣例:

 

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 的範圍內。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章