1. 程式人生 > >shunting-yard 調度場算法、中綴表達式轉逆波蘭表達式

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

iostream == c++代碼 返回結果 兩個棧 string 求解 波蘭表達式 入棧

中綴表達式

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;
}

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