# Ch18 Stack與Queue > 搭配 [virtual judge解題系統](https://vjudge.net/contest/273248) ## > 上一章:[二分搜尋法](https://hackmd.io/s/ByCZ5Yoa7) > 下一章:[BFS 廣度優先搜尋](https://hackmd.io/s/BkxmExS8J4) > 回目錄:[國立科學園區實驗中學C++程式語言自學講義](https://hackmd.io/s/B18yT_i5Z) ## <font color='darkblue'>什麼是Stack</font> 想像有一疊箱子 你可以一直增加新的箱子到這疊的頂端 也可以從這疊的頂端拿走箱子 但是你不能從中間塞箱子進去,因為這樣會弄垮 也不能從中間抽箱子出來,因為這樣也會弄垮 另外你也看不到中間是哪些箱子,只知道最上方是什麼 這座箱子塔就稱為「Stack」 ## <font color='darkblue'>程式語言的Stack</font> stack是一種「資料結構」 專門處理這種「愈晚放進來的要愈先拿走」的問題 ### 宣告一個stack ```cpp= #include<stack> stack<int> st; ``` 想要使用stack時 記得先include<stack>這個函式庫 尖括號內的`<int>`代表這個stack裡面放的都是整數 如果想要在stack裡面放其他型態的資料的話 也可以宣告成這樣 ```cpp= stack<float> st; stack<string> st; stack<char> st; ``` ### 放東西進去 現在你有了一個名為「st」的空塔了 你可以放東西進去 ```cpp= st.push(3); st.push(5); st.push(-10); ``` 這樣一來這個塔就有了三個數字 由底到頂分別是3、5、-10 -10會在最上面 ### 看看最上面是甚麼 這個塔只有最上面的數字是你看得到的 其他你都看不到了 ```cpp= int a = st.top(); ``` 這樣一來a就會是-10 ### 移除最頂端的一個 塔當然不是只有一直蓋而已 還可以把頂端丟掉 通常會在你把最頂端的物件拿來用完之後 就可以把它丟掉 ```cpp= st.pop(); ``` pop這個字用得真不錯 有一種把他擠掉的感覺 ### 檢查塔是不是空的 ```cpp= if(st.empty()) cout << "it is empty" << endl; else cout << "it contains something" << endl; ``` 記起來了嗎 會常用到的總共有四個功能 push、top、pop、empty 忘記了可以再回來看 那麼接下來就來做題目吧 ## <font color='darkblue'>Stack的應用</font> <font color='darkorange'>【例題】</font> > 天上降下各種顏色的磚塊 > 每種顏色會用一個數字來表示 > 這些磚塊降到地面時會疊成一疊 > 如果相同的顏色正好相鄰,他們就會一起消失 > 請問玩完後,這座塔裡沒消掉的磚塊數字總和是多少? sample input: 7 3 1 2 2 4 4 1 sample output: 3 每次拿到一個新磚塊時 先檢查它和塔頂的磚塊是否相同 如果塔原本就是空的,或是和塔頂不同:把這個新磚塊疊上去 如果和塔頂相同:把塔頂除掉 ```cpp= for(int i=0;i<n;i++){ cin >> new_tile; if(st.empty()||st.top()!=new_tile) st.push(new_tile); else st.pop(); } ``` 最後再來看看塔中所有的數字和是多少 由於我們只看得到塔頂的數字 因此我們要不斷地看塔頂、丟掉塔頂 直到塔被清空為止 ```cpp=9 ans=0; while(!st.empty()){ ans+=st.top(); st.pop(); } cout<<ans<<endl; ``` <font color="darkgreen"> 【學生練習題】</font> > - [ ] [A - Parentheses Balance](https://vjudge.net/contest/273248#problem/A) > > 第一行有一個數字n,代表接下來有n個字串 > 每一個字串都有一個獨立的答案 > > 每個字串都是一堆括弧 > 其中如果有()或[]就可以互相配起來 > 也可以用括弧包括弧 > 像是[ ( [ ] [ ] ( ) ) ] > 但也是有可能配不起來的 > 像是[ [ ) ) > 或是] ] [ [ > > 對於每一個字串 > 如果它能完整配完左右括弧,就輸出Yes > 否則輸出No ## <font color='darkblue'>什麼是Queue</font> 想像有一條隊伍 你可以一直增加新的人到這隊伍的尾端 也可以從這隊伍的前端拿走一個人 但是你不能從中間塞人進去,插隊太糟糕 也不能從中間抽人出來,他排得好好的你把他踢掉幹嘛!? 另外你也看不到中間是哪些人,只知道最前面是誰 這個隊伍,就稱為「Queue」 ## <font color='darkblue'>程式語言的Queue</font> queue是一種「資料結構」 專門處理這種「愈早放進來的要愈先拿走」的問題 ### 宣告一個queue ```cpp= #include<queue> queue<int> q; ``` 想要使用queue時 記得先include<queue>這個函示庫 尖括號內的`<int>`代表這個queue裡面放的都是整數 如果想要在queue裡面放其他型態的資料的話 也可以宣告成這樣 ```cpp= queue<float> q; queue<string> q; queue<char> q; ``` ### 放東西進去 現在你有了一個名為「q」的空隊伍了 你可以放東西進去 ```cpp= q.push(3); q.puah(5); q.push(-10); ``` 這樣一來這個隊伍就有了三個數字 由頭到尾分別是3、5、-10 3會在最前面 ### 看看最前面是甚麼 這個塔只有最前面的數字是你看得到的 其他你都看不到了 ```cpp= int a = q.front(); ``` 這樣一來a就會是3 ### 移除最前面的一個 隊伍當然不是只有一直變長而已 還可以把最前面丟掉 通常會在你把最前面的物件拿來用完之後 就可以把它丟掉 ```cpp= q.pop(); ``` pop這個字用得真不錯 有一種把他擠掉的感覺 ### 檢查隊伍是不是空的 ```cpp= if(q.empty()) cout << "it is empty" << endl; else cout << "it contains something" << endl; ``` 記起來了嗎 會常用到的總共有四個功能 push、front、pop、empty 忘記了可以再回來看 那麼接下來就來做題目吧 ## <font color='darkblue'>Queue的應用</font> <font color='darkorange'>【例題】</font> > 你有一疊撲克牌 > 原本依照1在最上面、接下來是2...這樣的順序排好 > 每次你都會念出最上面一張的數字,並把那張丟掉 > 然後再把第二章塞到牌的最下面 > 一直到整疊牌都被你丟完為止 > 請問你念出的這些數字依序是哪些? 首先先將這些牌依序塞進queue裡 ```cpp= for(int i=1;i<=n;i++){ q.push(i); } ``` 接下來,利用迴圈將這些牌丟光光 ```cpp=4 while(!q.empty()){ //念出第一張牌並丟掉 cout<<q.top()<<" "; q.pop(); //把第二張放到最後面(如果有的話) if(!q.empty()){ int last=q.top(); q.pop(); q.push(last); } } ``` <font color="darkgreen"> 【學生練習題】</font> > - [ ] [B - Throwing cards away I ](https://vjudge.net/contest/273248#problem/B) ## <font color='darkblue'>Queue的變形:Priority Queue</font> 想像一個隊伍中 每個人手上都有號碼牌 每次有新的人要加入隊伍時 都會檢查他的號碼 如果他的號碼比隊伍中的任何人都大 他就會無條件跑到隊伍的第一個 另外 當隊伍的第一個離開時 剩下的人裡面號碼最大的就要來成為新的排頭 這樣的「優先隊伍」 就稱作「priority queue」 ## <font color='darkblue'>程式語言的Priority Queue</font> priority_queue是一種「資料結構」 專門處理這種「號碼愈大的要愈先拿走」的問題 ### 宣告一個priority_queue ```cpp= #include<queue> priority_queue<int> pq; ``` 想要使用queue時 記得先include<queue>這個函示庫 尖括號內的`<int>`代表這個queue裡面放的都是整數 如果想要在queue裡面放其他型態的資料的話 也可以宣告成這樣 ```cpp= priority_queue<float> pq; priority_queue<string> pq; priority_queue<char> pq; ``` ### 放東西進去 現在你有了一個名為「pq」的優先隊伍了 你可以放東西進去 ```cpp= pq.push(3); pq.push(5); pq.push(-10); ``` 這樣一來這個隊伍就有了三個數字 5會在最前面 ### 看看最前面是甚麼 這個塔只有最前面的數字是你看得到的 其他你都看不到了 ```cpp= int a = pq.top(); ``` 這樣一來a就會是5 ### 移除最前面的一個 隊伍當然不是只有一直變長而已 還可以把最前面丟掉 通常會在你把最前面的物件拿來用完之後 就可以把它丟掉 ```cpp= pq.pop(); ``` pop這個字用得真不錯 有一種把他擠掉的感覺 ### 檢查隊伍是不是空的 ```cpp= if(pq.empty()) cout << "it is empty" << endl; else cout << "it contains something" << endl; ``` 記起來了嗎 會用到的總共有四個功能 push、top、pop、empty 忘記了可以再回來看 那麼接下來就來做題目吧 <font color='darkorange'>【例題】</font> > 有好多隻史萊姆 > 每隻的體積都不太一樣 > 每次都要抓最小的兩隻來合併 > 合併的時候要耗費的魔力就是這兩隻的體積之和 > 請問把這些史萊姆合成一隻大史萊姆總共最少需要多少魔力? 這跟Green Judge的某題一模一樣呢 首先先把這些史萊姆都放進priority_queue裡 ```cpp= for(int i=0;i<n;i++){ cin>>slime; pq.push(slime); } ``` 這時體積最大的史萊姆會跑到priority_queue的頂端 但是你其實想要讓小的在頂端 所以要稍微修改一下priority_queue宣告 ```cpp= priority_queue<int, vector<int>, greater<int> > pq; ``` 這樣一來愈小的史萊姆會在愈上面 所以你不需要特意去找最小的 只要拿priority_queue的頂端就好了 ```cpp= all_magic=0; while(!pq.empty()) { int a=pq.top(); pq.pop(); if(!pq.empty()){ int b=pq.top(); pq.pop(); int new_slime=a+b; all_magic+=new_slime; pq.push(new_slime); } } ``` <font color="darkgreen"> 【學生練習題】</font> > - [ ] [C - Add All ](https://vjudge.net/contest/273248#problem/C) <font color="darkgreen"> 【其他stack與queue的進階題】</font> > - [ ] [D - Rails ](https://vjudge.net/contest/273248#problem/D) > - [ ] [E - Printer Queue ](https://vjudge.net/contest/273248#problem/E) ## > 上一章:[二分搜尋法](https://hackmd.io/s/ByCZ5Yoa7) > 下一章:[BFS 廣度優先搜尋](https://hackmd.io/s/BkxmExS8J4) > 回目錄:[國立科學園區實驗中學C++程式語言自學講義](https://hackmd.io/s/B18yT_i5Z)