AcWing 828. 模擬棧
模板題:
實現一個棧,棧初始為空,支援四種操作:
push x
– 向棧頂插入一個數 x;pop
– 從棧頂彈出一個數;empty
– 判斷棧是否為空;query
– 查詢棧頂元素。
現在要對棧進行 MM 個操作,其中的每個操作 3 和操作 4 都要輸出相應的結果。
輸入格式
第一行包含整數 M,表示操作次數。
接下來 M 行,每行包含一個操作命令,操作命令為 push x
,pop
,empty
,query
中的一種。
輸出格式
對於每個 empty
和 query
操作都要輸出一個查詢結果,每個結果佔一行。
其中,empty
操作的查詢結果為 YES
或 NO
,query
操作的查詢結果為一個整數,表示棧頂元素的值。
資料範圍
1≤M≤1000001≤M≤100000,
1≤x≤1091≤x≤109
所有操作保證合法。
輸入樣例:
10
push 5
query
push 6
pop
query
pop
empty
push 4
query
empty
輸出樣例:
5
5
YES
4
NO
模擬棧的實現:
- 棧的特性:先進後出,後出先進;
- push x :棧頂所在索引往後移動一格,然後放入x。add[++t] = x;
- pop : top 往前移動一格。-- t;
- empty :top 大於等於 0 棧非空,小於 0 棧空。top == -1 ? “YES” : “NO”;
- query : 返回棧頂元素。st[top];
AC程式碼
#include<iostream>
using namespace std;
const int D=100015;
int add[D],t; //add[D]相當於一個棧,t就是一個"滑輪":實現增刪操作;
int main(){
int n; //初始化
cin >> n;
string s;
int m;
while(n--){
cin >> s;
//這裡實現把數值壓入棧中:“++t"開闢一個空間,把值賦上
if(s=="push"){
cin >> m;
add[++t]=m;
//
}else if(s=="pop"){
--t; //這裡同理:直接刪除這個空間;
}else if(s=="query"){
cout << add[t] << endl; //直接輸出(一系列的操作,滑輪停到那就輸出啥)
}else if(s=="empty"){
if(t>0){
cout << "NO" << endl;//有值就不為'No',無輸出'YES';
}else{
cout << "YES" << endl;
}
}
}
}
AcWing 829. 模擬佇列
模板題:
實現一個佇列,佇列初始為空,支援四種操作:
push x
– 向隊尾插入一個數 x;pop
– 從隊頭彈出一個數;empty
– 判斷佇列是否為空;query
– 查詢隊頭元素。
現在要對佇列進行 M 個操作,其中的每個操作 3 和操作 4 都要輸出相應的結果。
輸入格式
第一行包含整數 M,表示操作次數。
接下來 M 行,每行包含一個操作命令,操作命令為 push x
,pop
,empty
,query
中的一種。
輸出格式
對於每個 empty
和 query
操作都要輸出一個查詢結果,每個結果佔一行。
其中,empty
操作的查詢結果為 YES
或 NO
,query
操作的查詢結果為一個整數,表示隊頭元素的值。
資料範圍
1≤M≤1000001≤M≤100000,
1≤x≤1091≤x≤109,
所有操作保證合法。
輸入樣例:
10
push 6
empty
query
pop
empty
push 3
push 4
pop
query
push 6
輸出樣例:
NO
6
YES
4
解題思路:
佇列:隊尾入隊,對頭出隊;先進先出,後進後出;
圖解 :
先定義陣列 e[n] 儲存資料;定義一個 hh 代表隊頭,e[hh]就代表對頭元素,e[hh+1]是第二個出隊的元素;
在定義一個 tt 代表隊尾,e[tt]就是隊尾元素,e[tt+1]是第二個隊尾插入的元素;
這個就跟單連結串列思想是一致的,剛開始讓要插入的元素指向“-1”,進行刪除的時候,指向這個節點的後一個節點就好了;
咱們舉個例子:佇列中的元素
2 3 4 5
我現在要刪除 “ 2 ” 咱們是不是隻需要記錄 “2” 對頭的後一個元素就好了,就在下一次的query
操作輸出e[hh]就好了,hh 就只要負責 ++ 移到後一個數;
咱們要做的只是模擬,並不是向真正的資料結構一樣,實現刪除操作,咱們只需要做好模擬的事,不需要考慮它數值移到哪去了
AC程式碼
#include<iostream>
using namespace std;
const int N=100015;
//這裡的tt也可以定義0,那尾插就是tt++,hh<tt了(看個人習慣)
int e[N],hh,tt=-1;//hh對頭,tt隊尾
int main(){
int n;
cin >> n;
string s;
int m;
while(n--){
cin >> s;
if(s=="push"){
cin >> m;
e[++tt]=m;
}else if(s=="pop"){
hh++;
}else if(s=="query"){
cout << e[hh] << endl;
}else if(s=="empty"){
if(hh<=tt){
cout << "NO" << endl;
}else cout << "YES" << endl;
}
}
}
棧:表示式求值
實現棧思想例題:
給定一個表示式,其中運算子僅包含 +,-,*,/
(加 減 乘 整除),可能包含括號,請你求出表示式的最終值。
注意:
- 資料保證給定的表示式合法。
- 題目保證符號
-
只作為減號出現,不會作為負號出現,例如,-1+2
,(2+2)*(-(1+1)+2)
之類表示式均不會出現。 - 題目保證表示式中所有數字均為正整數。
- 題目保證表示式在中間計算過程以及結果中,均不超過 231−1231−1。
- 題目中的整除是指向 00 取整,也就是說對於大於 00 的結果向下取整,例如 5/3=15/3=1,對於小於 00 的結果向上取整,例如 5/(1−4)=−15/(1−4)=−1。
- C++和Java中的整除預設是向零取整;Python中的整除
//
預設向下取整,因此Python的eval()
函式中的整除也是向下取整,在本題中不能直接使用。
輸入格式
共一行,為給定表示式。
輸出格式
共一行,為表示式的結果。
資料範圍
表示式的長度不超過 105105。
輸入樣例:
(2+2)*(1+1)
輸出樣例:
8
棧引導:
- 現在給出一列算式 :
算式:1+1+1*5*6 //理解這一步:我們應該先算乘法嘛?這裡也可以看出,即使先算乘法,也不會影響 “1+1” 要執 =32 行這一步
- 那知道這個特殊情況:上圖解 :
解析:(1)如果棧頂是+,即將入棧的是+,棧頂優先順序高,需要先計算,再入棧;
(2)如果棧頂是+,即將入棧的是*,棧頂優先順序低,直接入棧;
(3)如果棧頂是*,即將入棧的是+,棧頂優先順序高,需要先計算,再入棧;
(4)如果棧頂是*,即將入棧的是*,棧頂優先順序高,需要先計算,再入棧;
- i=j-1;問題
例子:2*10-10000+24-(5*3)+(3*2) //在i=j-1之前的迴圈中,遍歷‘10’就讓,已經是甩開i了;
就是說:i下標2指向數值‘1’的時候,j已經遍歷完了‘10’,如果直接讓j=i的話,就會跳過一些數值,
實際情況自己模擬一下就知道了
AC程式碼
#include<iostream>
#include<algorithm>
#include <stack>
#include <unordered_map>
using namespace std;
const int N=100010;
//棧的特性:後進先出
stack<int> value; //定義一個棧容器:用來儲存int型數值
stack<char> symbol; //定義一個棧容器:用來儲存char型字元
//unordered_map:使用雜湊表來實現。它透過雜湊函式將鍵對映到表中的位置,
//從而快速訪問、插入和刪除元素。由於雜湊表的特性,unordered_map 不保證元素按照任何特定的順序儲存。
unordered_map<char, int> p{{'+',1},{'-',1},{'*',2},{'/',2}};
void add(){
//第二個入棧元素賦值
int a=value.top();
value.pop();
//第一個入棧元素
int b=value.top();
value.pop();
//提取符號操作
char op=symbol.top();
symbol.pop();
//進行一個計算操作
int ans=0;
if(op=='+') ans=b+a;
//為什麼是b-a? 這裡就要明白棧的特性了
if(op=='-') ans=b-a;
if(op=='*') ans=b*a;
if(op=='/') ans=b/a;
//完成操作再次壓入棧中,進行下一步運算
value.push(ans);
}
int main(){
string s;
cin >> s;
int len=s.size();
for(int i=0;i<len;i++){
//isdigit:C 庫函式 int isdigit(int c) 檢查所傳的字元是否是十進位制數字字元。
if(isdigit(s[i])){
int x=0,j=i;
//這裡使用迴圈就是看看數值字元型是幾位數,畢竟10都佔兩位,所以要迴圈
while(j<len && isdigit(s[j])){
x=x*10+s[j]-'0';
j++;
}
value.push(x);
i=j-1;
}else if(s[i]=='('){
symbol.push(s[i]);
}else if(s[i]==')'){
while(symbol.top()!='('){
add();
}
symbol.pop();
}else {
if(!symbol.empty() && p[symbol.top()]>=p[s[i]]){ //判斷棧頂元素優先順序大於新運算子
add();
}
symbol.push(s[i]);
}
}
//判斷運算棧是否為空,不為繼續運算
while(!symbol.empty()) add();
cout << value.top();
}