咱們今天廣泛使用計算器,在初級的計算器中,因爲計算機可沒有人那麼聰明,很難可以準確得判斷運算的優先級,因此在寫計算機的計算器的時候,咱們須要將得到的四則運算的表達式改寫爲逆波蘭式,方便計算機進行運算。ios
所謂的逆波蘭表示法(Reverse Polish notation, RPN, 或者逆波蘭記法),這是一種數學表達式方式,在逆波蘭記發中,全部操做符置於操做數的後面,所以也被稱爲後綴表示法。算法
就像是樹的搜索方式,有前序、中序、後序遍歷,在一棵樹中,咱們將操做符放在節點處,將操做數放在葉子處,優先級越高的操做越靠下,咱們普通四則運算的表達式就是使用中序遍歷獲得的式子,而逆波蘭式則是經過後序遍歷獲得的,舉個例子:中綴表達式(a+b)*c-(a+b)/e的逆波蘭式是ab+c*ab+e/-。學習
要使用逆波蘭式進行運算,首先咱們須要知道如何將一個普通的四則運算表達式轉換爲一個標準的逆波蘭式,在算法書上都有講的,也已是一個很成熟,很方便的算法了。spa
在理解逆波蘭式的時候,咱們使用了樹的中序遍歷輔助咱們理解,可是在正式使用的時候,你們千萬不要想着先將普通的四則表達式生成一棵樹,而後再進行後序遍歷生成逆波蘭式,這是二逼作的事哈。我幫你們找了一個很簡潔,很易懂的哈:code
Step 1:咱們使用兩個棧構建逆波蘭式,棧S1用於臨時儲存運算符號,運算符在棧內遵循越往棧頂優先級越高的原則;棧S2用於輸入逆波蘭式。因爲咱們須要判斷式子是否讀完,因此先在S1內放入一個優先級最低的運算符,你們隨意哈;orm
Step 2:從表達式的左端開始逐個讀取字符x,進行以下步驟:get
一、 若x是1到9內的數,那麼繼續讀,直到讀出完整的操做數,而後將操做數壓入棧S2;數學
二、 若x是運算符,則分狀況討論:string
若x是'(',則直接壓入棧s1;it
若x是')',則將距離棧s1棧頂的最近的'('之間的運算符,逐個出棧,依次壓入棧s2,此時拋棄'(';
若x是除'('和')'外的運算符,則再分以下狀況討論:
若當前棧s1的棧頂元素爲'(',則將x直接壓入棧s1;
若當前棧s1的棧頂元素不爲'(',則將x與棧s1的棧頂元素比較,若x的優先級大於棧s1棧頂運算符優先級,則將x直接壓入棧s1。否者,將棧s1的棧頂運算符彈出,壓入棧s2中,直到棧s1的棧頂運算符優先級別低於(不包括等於)x的優先級,或棧s2的棧頂運算符爲'(',此時再則將x壓入棧s1;
Step 3:在進行完(2)後,檢查棧s1是否爲空,若不爲空,則將棧中元素依次彈出並壓入棧s2中(不包括最末尾的標誌符);
Step 4:完成上述步驟後,棧s2便爲逆波蘭式輸出結果。可是棧s2應作一下逆序處理,由於此時表達式的首字符位於棧底;
能順利生成逆波蘭式得話基本就大功告成了,相對於生成波蘭式,使用波蘭式計算那就是再簡單不過的了,建議使用一個棧存取操做數,而後順序讀就能夠了,在下面的代碼中,有實現,你們能夠看一下。
這裏附上作練習時寫的一個計算器的代碼,逆波蘭式的練習哈:
#include<iostream> #include<stack> using namespace std; class Calculator { // 整數的加減乘除運算器 private: string Polish; long int result; public: Calculator() { } long int& getResult(string exp) { Polish.clear(); stack<char> s1,s2; s1.push('#'); //以#號做爲標記 char temp; for(int i=0; i<exp.length(); i++) { //利用兩個堆棧生成逆波蘭式 if (exp[i] >= '0' && exp[i] <= '9') { s2.push(exp[i]); } else if (exp[i] == '+' || exp[i] == '-' ||exp[i] == '*' ||exp[i] == '/') { s2.push('#'); temp = s1.top(); if(temp == '#'||(temp == '+' || temp == '-')&&(exp[i] == '*' ||exp[i] == '/')) { s1.push(exp[i]); } else { while (!(temp=='#'||temp == '(' || (temp == '+' || temp == '-') && (exp[i] == '*' ||exp[i] == '/'))) { s2.push(temp); s1.pop(); temp = s1.top(); } s1.push(exp[i]); } } else if(exp[i] == ')'|| exp[i] == '(') { if(exp[i] == '(') { s1.push(exp[i]); } else { temp = s1.top(); while (temp != '(' ) { s2.push(temp); s1.pop(); temp = s1.top(); } s1.pop(); } } } while(s1.top()!='#') { s2.push(s1.top()); s1.pop(); } for(;s2.size()>=1;) { Polish.push_back(s2.top()); s2.pop(); } long int temp2; long int temp3; long int temp1=0; stack<long int> s3; bool sign =0; for(int i = Polish.length()-1; i >= 0; i--) { //進行逆波蘭式的運算 if(Polish[i] >= '0' && Polish[i] <= '9') { temp1 = temp1*10 + Polish[i]-'0'; sign = 1; } if((Polish[i] == '#'||Polish[i] == '+'||Polish[i] == '-'||Polish[i] == '*'||Polish[i] == '/')&&(sign == 1)||(i == 0&&sign == 1)) { if(sign == 1){ s3.push(temp1); temp1 = 0; sign = 0; } } if(Polish[i] == '+'||Polish[i] == '-'||Polish[i] == '*'||Polish[i] == '/') { temp2 =s3.top(); s3.pop(); temp3 =s3.top(); s3.pop(); switch(Polish[i]) { case '+': s3.push(temp3+temp2);break; case '-': s3.push(temp3-temp2);break; case '*': s3.push(temp3*temp2);break; case '/': s3.push(temp3/temp2);break; } } } result = s3.top(); return result; } }; int main() { Calculator c; //對各類極端狀況進行驗證 cout<<c.getResult("3")<<endl; cout<<c.getResult("((3+4)*5+6)*7")<<endl; cout<<c.getResult("1+2*3")<<endl; cout<<c.getResult("2*5+10")<<endl; cout<<c.getResult("3*(5+4)")<<endl; cout<<c.getResult("(4+5)*(2+2)")<<endl; cout<<c.getResult("1/2+1/2")<<endl; cout<<c.getResult("0+0")<<endl; cout<<c.getResult("4*5-7*8")<<endl; cout<<c.getResult("378+456-500*12/2")<<endl; cout<<c.getResult("((11*(12+13)*(14+15))+(16+17))*(18+19)")<<endl; cout<<c.getResult("4+5")<<endl; cout<<c.getResult("4")<<endl; cout<<c.getResult("77+44-22*33/11")<<endl; return 0; }
一點點學習心得:也許你們都不太喜歡研究這些看似比較腦殘的東西哈,可是,親身寫了以後,我以爲這貨真是考驗一我的的思惟的清晰程度,並且,若是僅僅是看算法,我以爲不論背得多熟,也老是雲裏霧裏的,仍是須要本身動手寫一寫,感覺感覺。反正代碼嘛,多寫寫總沒什麼壞處,特別是這種特別繞人的東西,寫了後再看其它的算法,理解起來事半功倍哈。