今天總結一下棧的一個重要應用---四則數學表示式的求解
數學表示式的求解是棧的一個重要的應用,在計算機的應用中
如果求解一個四則運算表示式,我們可能會直接寫一個程式例如什麼printf("%d",a+b)這些類似的簡單程式碼實現加減乘除運算
但如果給你一個這樣的表示式:9+(3-1)*3+10/2,這樣的表示式對於計算機的困難點是乘除號出現在了加減號的後面,並且加上括號就更加麻煩了,
而只識別01的計算機可能會只按照式子從左往右挨個計算,這就忽略了四則運算表示式的按順序計算,因此,我們需要設計一種演算法來實現對於這類四則運算表示式的求解
我們都知道,對於有左括號的式子一定會有右括號但是有右括號的式子卻不一檔有左括號,因此我們需要一種儲存的資料結構來實現逆序儲存和匹配,因此就用了棧這種資料結構;
遇到左括號就進站,出現右括號左括號出戰並且讓數字參與運算;
但是很遺憾的是,括號也是四則運算式的一部分,有沒有一種方法可以讓括號不出現在運算式中呢?
答案是有的:
有一種表示式被稱為是逆波蘭表示式的表示式可以避免括號的出現,這種表示式不需要括號出現,並且是一種字尾表示式
字尾之地方在於運算子是出現在運算數之後的
例如這樣的表示式
像這樣,就避免了括號的出現,使運算更加簡單;
那怎麼將中綴表示式轉成逆波蘭表示式呢?
轉換的標準是醬紫的:
碰到數字就輸出,如果是運算子先判斷與棧頂符號的優先順序,是右括號或者優先順序小於等於棧頂符號的,將棧頂元素一次輸出並且彈棧,並且把當前符號進棧,直到輸出字尾表示式;
當然這樣說顯得很籠統,我總結了一下記憶的方式:
按照這樣的法則就可以將中綴表示式轉成逆波蘭表示式啦;
來舉個例子:9+(3-1)*3+10/2
轉化方式和步驟是醬紫的,
我們接下來用程式碼實現一下:
1 #include<bits/stdc++.h> 2 using namespace std; 3 stack<char>q; 4 string s; 5 int main() 6 { 7 std::ios::sync_with_stdio(false); 8 cin.tie(0); 9 cout.tie(0); 10 getline(cin,s); 11 for(register int i=0;i<s.size();i++) 12 { 13 if(s[i]>='a'&&s[i]<='z')//碰到數字直接輸出 14 cout<<s[i]; 15 else if(s[i]==')')//如果碰到右括號,就找到左括號與它相匹配 16 { 17 while(true) 18 { 19 if(q.top()=='(')//碰到了就彈棧並且停止 20 { 21 q.pop(); 22 break; 23 } 24 cout<<q.top(); 25 q.pop();//沒找到之前一直彈棧並輸出 26 } 27 } 28 else//運算子 29 { 30 if(!q.empty()&&(s[i]=='+'||s[i]=='-')&&(q.top()=='*'||q.top()=='/'))//碰到加號或者減號 31 { 32 while(!q.empty()) 33 { 34 if(q.top()=='(')//碰到括號就停止 35 break; 36 cout<<q.top();//一直彈棧並輸出 37 q.pop(); 38 } 39 q.push(s[i]);//當前的元素入棧 40 } 41 else 42 q.push(s[i]);//其餘的入棧 43 } 44 } 45 while(!q.empty()) 46 { 47 cout<<q.top(); 48 q.pop(); 49 } 50 return 0; 51 }
逆波蘭表示式轉化成功了,怎麼求逆波蘭表示式的值呢:
很簡單,從頭開始掃描,如果碰到數字就進站,
如果碰到運算子,將棧頂的兩個數字出站進行運算,在將結果入棧,一直得到最終的結果;
圖解演示一下吧:
相應的用程式碼實現是灰常簡單的:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int num=1e4+10; 4 char ch[num]; 5 int n; 6 stack<int>q; 7 bool operatorpoland(char op)//判段是不是運算子 8 { 9 if(op=='+'||op=='-'||op=='*'||op=='/') 10 return true; 11 else 12 return false; 13 } 14 int reversepoland() 15 { 16 int a,b; 17 for(register int i=0;i<n;i++) 18 { 19 if(!operatorpoland(ch[i]))//如果不是運算子 20 { 21 q.push((ch[i]-'0'));//入棧 22 } 23 else 24 { 25 int a=q.top();//取棧頂兩個元素進行運算 26 q.pop(); 27 int b=q.top(); 28 q.pop(); 29 if(ch[i]=='+')//如果運算子是加號 30 q.push(a+b); 31 else if(ch[i]=='-')//如果運算子號是減號 32 q.push(a-b); 33 else if(ch[i]=='*')//乘號 34 q.push(a*b); 35 else if(ch[i]=='/')//除號 36 q.push(a/b); 37 } 38 } 39 return q.top();//最後得到棧頂的元素即是結果 40 } 41 int main() 42 { 43 std::ios::sync_with_stdio(false); 44 cin>>ch; 45 n=strlen(ch); 46 printf("%d",reversepoland()); 47 return 0; 48 }
可以拿兩道題目練練手:
https://acm.sdut.edu.cn/onlinejudge3/contests/3980/problems/F
http://acm.hdu.edu.cn/showproblem.php?pid=1237(水卻處理起來有些麻煩)
一會會看情況把沒學完的kmp演算法給補上;
那就先到這裡啦~~~~