逆波蘭式
一.前言:
逆波蘭表示法 ( Reverse Polish notation , RPN ,或 逆波蘭記法 ),是一種是由 波蘭 數學家 揚·武卡謝維奇 1920年引入的數學表示式形式,在逆波蘭記法中,所有 操作符 置於 運算元 的後面,因此也被稱為 字尾表示法 (也稱字尾表示式)。而我們平常用的a+b叫做中綴表示式,轉換成字尾表示式為ab+(其實還有一個波蘭式,也就作字首表示式+ab),為什麼要這麼轉換呢?雖然我們人腦非常喜歡這種顯而易見的中綴表示式計算,但是計算機算覺得這非常的難計算(主要是堆疊的實現會變的比中綴表示式更簡單),因此就有了波蘭表示式和逆波蘭表示式。
二.問題分析:
那我們如和將中綴表示式轉成字尾表示式呢?總所周知,在中綴表示式中我們的操作符+-優先順序最低,*/優先順序第二,而()優先順序最高。但是在逆波蘭式的轉換過程中這個優先順序有一些變化,乘除的優先順序會看成比加減的優先順序更低,括號優先順序依舊是最高,因此我們可以有定義+-優先順序為2,()優先順序為1,*/優先順序為3。因此我們就解決掉了優先順序的問題。
其他規則
1.如果碰到左括號,則直接入棧。
2.如果碰到優先順序高於棧頂的,則依次出棧,直到棧頂為空或者棧頂的優先順序高於碰到的操作符,然後再把操作符加入棧區即可
3.如果中綴表示式遍歷完成後,棧區還剩餘操作符,則直接把剩餘操作符從棧頂依次加入逆波蘭式
4.如果碰到右括號,則把括號和右括號之間的操作符加入逆波蘭式,左括號和右括號出棧
接下來我們看看具體怎麼轉換吧!
首先,我們給出一個計算樣例來解釋計算過程,中綴表示式如下:1+2*(3+4)/5
我們給出一個棧區用來存操作符,用一個字串來儲存逆波蘭式
碰到1,直接加入逆波蘭式
stack(代表棧區):NULL
bolan(代表逆波蘭式):1
碰到‘+’,因為棧區此時為空,故直接加入棧區
stack(代表棧區):+
bolan(代表逆波蘭式):1
碰到2,直接加入逆波蘭式
stack(代表棧區):NULL
bolan(代表逆波蘭式):1,2
碰到‘*’,因為*的優先順序為2,最低,故不需要彈出+號,因此直接入棧\
stack(代表棧區):+,*
bolan(代表逆波蘭式):1,2
碰到‘(’,因為左括號需要續')'相匹配,故直接入棧
stack(代表棧區):+,*,(
bolan(代表逆波蘭式):1,2
碰到數字3,直接加入逆波蘭式
stack(代表棧區):+,*,(
bolan(代表逆波蘭式):1,2,3
碰到‘+’,因為+優先順序低於括號,因此直接入棧
stack(代表棧區):+,*,(,+
bolan(代表逆波蘭式):1,2,3
碰到數字4,直接加入逆波蘭式
stack(代表棧區):+,*,(,+
bolan(代表逆波蘭式):1,2,3,4
碰到‘(',把左括號之前的全部加入逆波蘭式
stack(代表棧區):+,*
bolan(代表逆波蘭式):1,2,3,4,+
碰到’/',把棧頂優先順序不高於'/'的全部加入逆波蘭式,再把自己加入棧區
stack(代表棧區):+,/
bolan(代表逆波蘭式):1,2,3,4,+,*
碰到數字5,直接加入波蘭式
stack(代表棧區):+,/
bolan(代表逆波蘭式):1,2,3,4,+,*,5
字串遍歷結束,把棧區剩下的全部加入逆波蘭式
stack(代表棧區):NULL
bolan(代表逆波蘭式):1,2,3,4,+,*,5,/,+
三.程式碼實現:
1 #include "bits/stdc++.h" 2 using namespace std; 3 int jdgCode(char flag)//定於操作符運算 4 { 5 if(flag == '+') 6 return 1; 7 if(flag == '-') 8 return 2; 9 if(flag == '*') 10 return 3; 11 if(flag == '/') 12 return 4; 13 } 14 double Compute(string cpu) 15 { 16 stack <double> ans; 17 for(auto iter = cpu.begin();iter != cpu.end();iter++){ 18 if(*iter >= '0' && *iter <= '9') 19 ans.push(*iter - '0'); 20 else { 21 double r1,r2; 22 r1 = ans.top(); 23 ans.pop(); 24 r2 = ans.top(); 25 ans.pop(); 26 switch(jdgCode(*iter)){ 27 case 1: ans.push(r2 + r1);break; 28 case 2: ans.push(r2 - r1);break; 29 case 3: ans.push(r2 * r1);break; 30 case 4: ans.push(r2 / r1);break; 31 } 32 } 33 } 34 return ans.top(); 35 } 36 int prio(char a) 37 { 38 if(a == '*' || a == '/') 39 return 2; 40 else if(a == '+' || a == '-') 41 return 1; 42 else 43 return 0;//左括號優先順序 44 } 45 void Dispose(string& ms,string& as)//處理成字尾表示式 46 { 47 stack <char> ct;//定義一個ct容器儲存運算子 48 for(auto iter = ms.begin();iter != ms.end();iter++){ 49 if(*iter >= '0' && *iter <= '9')//如果是數字,直接入棧 50 as += *iter; 51 else{ 52 if(ct.empty() || *iter == '(')//如果是空棧,或者是左括號,直接入棧 53 ct.push(*iter); 54 else if(*iter == ')'){ 55 while(ct.top() != '('){//把左括號和有括號裡面的操作符出棧 56 as += ct.top(); 57 ct.pop(); 58 } 59 ct.pop();//彈出左括號 60 } 61 else{ 62 while(!ct.empty() && prio(*iter) <= prio(ct.top())){//把優先順序低於當前操作符的操作符出棧 63 as += ct.top(); 64 ct.pop(); 65 } 66 ct.push(*iter);//把當前操作符入棧 67 } 68 } 69 } 70 while(!ct.empty()){//如果棧區還有操作符 71 as += ct.top(); 72 ct.pop(); 73 } 74 } 75 int main() 76 { 77 string ms;//中綴表示式 78 string as;//字尾表示式 79 cin >> ms; 80 Dispose(ms,as);//將中綴表示式處理為字尾表示式 81 cout << as << endl;//輸出處理後的字尾表示式 82 cout << "Result:" << Compute(as) << endl;//輸出字尾表示式計算結果 83 return 0; 84 }