shunting-yard 調度場算法、中綴表達式轉逆波蘭表達式


 

中綴表達式

1*(2+3)

這就是一個中綴表達式,運算符在數字之間,計算機處理前綴表達式和后綴表達式比較容易,但處理中綴表達式卻不太容易,因此,我們需要使用shunting-yard Algorithm(調度場算法)來將中綴表達式轉換為后綴表達式(即逆波蘭表達式),然后求解。

上面的中綴表達式轉后綴表達式后為:

1 2 3 + *

調度場算法

為了將中綴表達式轉為后綴表達式,使用調度場算法,算法思想如下:

  准備兩個棧,一個用於存放數字,一個用於存放操作符。

  從左到右遍歷表達式,如果是數字,直接入棧。(注意數字可能是多位數甚至小數)

  如果是符號:

    如果棧為空,或者當前符號為‘ ( ’,或者棧頂為' ( ',直接入棧。

    如果當前運算符優先級高於棧頂運算符的優先級,則入棧。(注意是高於,等於也不行)

    如果當前運算符優先級不高於棧頂運算符,先從存符號的棧中取出一個操作符,從存數據的棧中取出兩個數字,將它們做一次運算並將結果壓入數據棧,最后再把當前運算符壓入符號棧。

    如果當前符號是' ) ',不斷重復上面划線部分的操作,直到取出的操作符是' ( '為止。

  表達式遍歷完后,不斷重復上面划線部分的操作,直到符號棧為空,此時數據棧還有一個數字,那就是原表達式的值。

這樣就基本完成了。

這里再來討論一下負號的處理,如何判斷 ' - ' 是減號還是負號呢?

  如果' - '出現在表達式開頭,一定是負號。

  如果' - '出現在數字后面,一定是減號。

  如果' - '出現在' ( '后面,一定是負號。

  如果' - '出現在' ) '后面,一定是減號。

綜上可以看出,只要‘ - ’在表達式開頭或者' ( '后面就可以判斷為負號,但是判斷之后怎么處理呢?

有兩種辦法,一種是在判斷為負號的時候將數字0壓入數據棧,然后將負號壓入符號棧,可以看做是零減去一個數字。

另一種辦法是,如果判斷為負號,將下一個壓入數據棧的數字乘上 -1。采用這種方法需要注意,如果括號內只有一個負數而沒有計算式,按照上面的判斷法則,下一個' ) '將直接被壓入棧,從而導致奇怪的事情發生,只要在' ) '入棧前特判一下即可避免這種問題。(下面代碼采用第二種方法)

 

C++代碼實現如下

 

#include <iostream> #include <string> #include <cstring> #include <stack>
using namespace std; int op[55];         //確定運算符的優先級

/* string轉數字 */
double toDig(string str) { double n = 0, mag = 0.1; for(int i = 0; i < str.length(); i++) { if(str[i]=='.') break; mag *= 10; } for(int i = 0; i < str.length(); i++) { if(str[i]=='.') continue; n += mag*(str[i] - '0'); mag /= 10; } return n; } /* 計算並返回結果 */
double getAns(double a, double b, char c) { switch (c) { case '+': return b+a; case '-': return b-a; case '*': return b*a; case '/': return b/a; } } /* shunting-yard */
double shunting(string str) { stack<double > iStk; stack<char> strStk; for(int i = 0; i < str.length();) { if( (str[i]>='0' && str[i]<='9') || (i==0&&str[i]=='-') || (str[i]=='-'&&str[i-1]=='(')) { // 判斷是否為數字或負號
            string s1; int f = 1; if(str[i]=='-') { f=-1; i++; } while((str[i]>='0' && str[i]<='9') || str[i]=='.') { s1 += str[i++]; } iStk.push(f*toDig(s1)); } else {    //不是數字或負號,則為操作符或括號 // 如果棧為空、或操作符為'('、或棧頂為'('、或當前操作符的優先級大於棧頂操作符,則操作符入棧
            if(strStk.empty() || str[i]=='(' || strStk.top()=='(' || (str[i]!=')'&&op[str[i]] > op[strStk.top()])) { if(str[i]==')' && strStk.top()=='(') strStk.pop();  //當前符號和棧頂是一對括號則消除它們
                else strStk.push(str[i]); } else if(str[i] == ')') { // 如果當前是')',則做運算直到棧頂是'('
                char c = strStk.top(); while(c != '(') { double a = iStk.top(); iStk.pop(); double b = iStk.top(); iStk.pop(); strStk.pop(); iStk.push(getAns(a,b,c)); c = strStk.top(); } strStk.pop(); } else { // 否則,說明當前運算符優先級等於或小於棧頂運算符,將棧頂操作符取出做一次運算,將運算結果壓棧,最后再將當前操作符入棧
                double a = iStk.top(); iStk.pop(); double b = iStk.top(); iStk.pop(); char c = strStk.top(); strStk.pop(); iStk.push(getAns(a,b,c)); strStk.push(str[i]); } i++; } } // 表達式處理完后,不斷運算直到操作符空棧,此時數據棧剩下的一個數據就是最終結果
    while(!strStk.empty()) { double a = iStk.top(); iStk.pop(); double b = iStk.top(); iStk.pop(); char c = strStk.top(); strStk.pop(); iStk.push(getAns(a,b,c)); } return iStk.top(); } int main() { string s; memset(op,1,sizeof(op)); op['+'] = op['-'] = 0; op['*'] = op['/'] = 1; while(cin >> s) { cout << shunting(s) << endl; } return 0; }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM