棧的現實應用很多,這裏記錄下用棧解決四則運算表達式求值的相關題目。
平常見到的公式如“7+5*4”,稱作中綴表達式,因爲所有的運算符號都在兩數字的中間,而對於計算機來說,中綴表達式是無法直接解析的。針對這個問題,20世紀50年代,波蘭邏輯學家 Jan ukasiewicz,創造了一種不需要括號的後綴表達法,稱爲逆波蘭( Reverse Polish Notation,RPN)表示。這種後綴表示法,是表達式的一種新的顯示方式,非常巧妙地解決了程序實現四則運算的難題。
以“9+(3-1)×3+10÷2”爲例,如果要用後綴表示法應該是2:“9 3 1-3 * + 1 0 2 / +”,這樣的表達式稱爲後綴表達式,叫後綴的原因在於所有的符號都是在要運算數字的後面出現。這種表達不適宜人去使用,但是卻很適合計算機。
一、中綴表達式轉後綴表達式
以上式爲例,中綴表達式“9+(3-1)×3+10÷2”轉化爲後綴表達式“9 3 1 - 3 * + 1 0 2 / +"。
-
初始化一空棧,用來對符號進出棧使用。
-
第一個字符是數字9,輸出9,後面是符號“+",進棧。步驟1 2如下圖所示
-
第三個字符是“(”,依然是符號,因其只是左括號,還未配對,故進棧。。
-
第四個字符是數字3,輸出,總表達式爲93,接着是“-”,進棧。步驟 3 4如下圖所示。
-
接下來是數字1,輸出,總表達式爲931,後面是符號“)”,此時,我們需要去匹配此前的“(",所以棧頂依次出棧,並輸出,直到“(”出棧爲止。此時左括號上方只有“一”,因此輸出“-”。總的輸出表達式爲931-。。
-
接着是數字3,輸出,總的表達式爲931-3.緊接着是符號“×”,因爲此時的棧頂符號爲“+”號,優先級低於“×”,因此不輸出,“*”進棧。步驟5 6如下圖所示。
-
7.之後是符號“+",此時當前棧頂元素比這個“+”的優先級高,因此棧中元素出棧並輸出(沒有比“+”號更低的優先級,所以全部出棧),總輸出表達式爲。然後將當前這個符號“+”進棧。也就是說,前6張圖的棧底的“+”是指中綴表達式中開頭的9後面那個“+",左圖中的棧底(也是棧頂)的“+”是指“9+(3-1)×3+”中的最後一個“+
-
緊接着數字10,輸出,總表達式變爲931-3*+10.後是符號“÷”,所以“/”進棧。步驟7 8如下圖所示。
-
最後一個數字2,輸出,總的表達式爲9 3 1 - 3 * + 1 0 2.如圖49-10的左圖所示。*
-
因已經到最後,所以將棧中符號全部出棧並輸出。最終輸出的後綴表達式結果爲9 3 1 - 3 * + 1 0 2 / +。步驟9 10如下圖所示。
要想讓計算機具有處理我們通常的標準(中綴)表達式的能力,最重要的就是兩步:
1.將中綴表達式轉化爲後綴表達式(棧用來進出運算的符號)。
2.將後綴表達式進行運算得出結果(棧用來進出運算的數字)。
二、後綴表達式計算
以上式爲例,後綴表達式計算的流程如下:
- 初始化一個空棧。此棧用來對要運算的數字進出使用。
- 後綴表達式中前三個都是數字,所以9、3、1進棧,如下圖所示
3. 接下來是“-”,所以將棧中的1出棧作爲減數,3出棧作爲被減數,並運算3-1得到2,再將2進棧,
4. 接着是數字3進棧,步驟3 4如下圖所示。
5. 後面是“*”,也就意味着棧中3和2出棧,2與3相乘,得到6,並將6進棧。
6. 下面是“+”,所以棧中6和9出棧,9與6相加,得到15,將15進棧,步驟 5 6如下圖所示。
7. 接着是10與2兩數字進棧。
8. 接下來是符號“/”,因此,棧頂的2與10出棧,10與2相除,得到5,將5進棧,步驟7 8如下圖所示。
9. 最後一個是符號“+",所以15與5出棧並相加,得到20,將20進棧,
10. 結果是20出棧,棧變爲空,步驟9 10 如下圖所示。
三、示例(後續會更新)
1. poj 2694 逆波蘭表達式(遞歸)
描述
逆波蘭表達式是一種把運算符前置的算術表達式,例如普通的表達式2 + 3的逆波蘭表示法爲+ 2 3。逆波蘭表達式的優點是運算符之間不必有優先級關係,也不必用括號改變運算次序,例如(2 + 3) * 4的逆波蘭表示法爲* + 2 3 4。本題求解逆波蘭表達式的值,其中運算符包括+ - * /四個。
輸入
輸入爲一行,其中運算符和運算數之間都用空格分隔,運算數是浮點數。
輸出
輸出爲一行,表達式的值。
可直接用printf("%f\n", v)輸出表達式的值v。
樣例輸入
* + 11.0 12.0 + 24.0 35.0
樣例輸出
1357.000000
提示
可使用atof(str)把字符串轉換爲一個double類型的浮點數。atof定義在math.h中。
此題可使用函數遞歸調用的方法求解
代碼:
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
char a[100];
double expression(){
scanf("%s",a);
switch (a[0]){
case '+': return expression()+expression();
case '-': return expression()-expression();
case '*': return expression()*expression();
case '/': return expression()/expression();
default: return atof(a);
}
}
int main(){
double ans=expression();
printf("%lf\n", ans);
}
2. LeetCode 面試題 16.26. 計算器
給定一個包含正整數、加(+)、減(-)、乘(*)、除(/)的算數表達式(括號除外),計算其結果。
表達式僅包含非負整數,+, - ,*,/ 四種運算符和空格 。 整數除法僅保留整數部分。
示例 1:
輸入: "3+2*2"
輸出: 7
示例 2:
輸入: " 3/2 "
輸出: 1
示例 3:
輸入: " 3+5 / 2 "
輸出: 5
說明:
你可以假設所給定的表達式都是有效的。
請不要使用內置的庫函數 eval。
因爲不用考慮括號,只有乘除法大與加減法的運算邏輯,代碼要簡潔很多,簡潔代碼原出處
class Solution {
public:
int calculate(string s) {
char op = '+';
int val;
istringstream iss(s);
stack<int> st;
while(iss>>val){
if(op=='+'){
st.push(val);
}else if(op=='-'){
st.push(-val);
}else{
int val2 = st.top(); st.pop();
if(op=='*') st.push(val*val2);
else if(op=='/') st.push(val2/val);
}
iss>>op;
}
int res = 0;
while(st.size()){
res += st.top(); st.pop();
}
return res;
}
};
參考:《大話數據結構》