中綴表達式與后綴表達式


        計算中綴表達式”可以稱得上是一個特別經典的關於棧的算法題,幾乎在所有數據結構教材中都會涉及,而且很多公司面試或者筆試的時候都會把這道題作為一個考察點。可以說,這是一道必須要掌握的算法題。中綴表達式、后綴表達式等概念在這里就不贅述了,讓我們直奔主題。
題目:輸入一個中綴表達式,計算其結果。
輸入的前提假設:
(1)只考慮+、-、*、/這四種運算符,中綴表達式中只有一種括號:();
(2)輸入的中綴表達式中只有整數,沒有小數;
(3)假定輸入是合法的。
很多文章或課本喜歡一步到位,直接討論如何從中綴表達式計算結果。但對於初學者來說,跨度未免大了點。這里循序漸進,從易到難,先討論如何將中綴表達式轉化為后綴表達式,再討論如何計算后綴表達式。最后在前面兩步的基礎上,討論如何一步到位,直接計算中綴表達式的結果:
一、如何將中綴表達式轉化為后綴表達式
        在日常應用中,算術表達式中運算符總是出現在兩個操作數之間,例如5*(7-2*3)+8/2,這種形式稱為中綴表達式。計算一個中綴表達式需要知道運算符的優先級和結合性。乘除是高優先級,加減是低優先級,優先級相同時他們都是左結合的,也就是從左計算到右。有括號就要計算括號內的表達式。
中綴表達式利於人的理解,但不便於計算機的處理。因此需要將中綴表達式轉換成后綴表達式,以方便計算機處理。所謂后綴表達式就是將運算符放在運算數之后。后綴表達式也稱為逆波蘭表達式。
比如:
中綴表達式為:1+(2-3)*4+4/2
對應后綴表達式為:1 2 3 - 4* + 4 2 / +
如何將一個中綴表達式轉化為后綴表達式?我們需要借助棧的力量,用它來存放運算符。算法流程如下:
首先將各種運算符(包括括號)的優先級排列如下(數字越大,優先級越高):
1:(
2:+ -
3:* /
4:)
對輸入的中綴表達式從左到右遍歷:
1)如果遇到數字,直接添加到后綴表達式末尾;
2)如果遇到運算符+、-、*、/:
先判斷棧是否為空。若是,則直接將此運算符壓入棧。若不是,則查看當前棧頂元素。若棧頂元素優先級大於或等於此操作符級別,則彈出棧頂元素,將棧頂元素添加到后綴表達式中,並繼續進行上述判斷。如果不滿足上述判斷或者棧為空,將這個運算符入棧。要注意的是,經過上述步驟,這個運算符最終一定會入棧。
3)如果遇到括號:
如果是左括號,直接入棧。如果是右括號,彈出棧中第一個左括號前所有的操作符,並將左括號彈出。(右括號別入棧)。
4)字符串遍歷結束后,如果棧不為空,則彈出棧中所有元素,將它們添加到后綴表達式的末尾,直到棧為空。
二、計算后綴表達式
后綴表達式的計算就相當簡單了。准備一個數字棧。從左到右掃描后綴表達式,如果是數字,放入數字棧。如果是符號,從數字棧中彈出兩個數字,第一個取出的數字為右運算數,第二個為左運算數,進行運算。然后將結果放進數字棧中。如此反復,直到讀完整個表達式后,留在數字棧中的那個數字就是最終結果。
C++代碼如下,要注意,下面的代碼默認中綴表達式中所有數字都是整數,並且都在0到9之間。而且計算結果都是整數(比如5/2=2)。

#include<iostream>  
#include<string>  
#include<stack>  
  
using namespace std;  
  
int getPriority(char ch)  
{  
    //獲取優先級  
    if (ch == '(') return 1;  
    else if (ch == '+' || ch == '-') return 2;  
    else if (ch == '*' || ch == '/') return 3;  
    else return 4;  
}  
  
string getPostfixExpression(string str)  
{  
    //將中綴表達式轉化為后綴表達式  
    //默認輸入是合法的  
    stack<char> mystack;  
    int size = str.size();  
    int i = 0;  
    char tmp;  
    string res = "";  
    while (i < size) {  
        if (str[i] >= '0' && str[i] <= '9'){  
            res.push_back(str[i]);  
        }  
        elseif (str[i] == '+' || str[i] == '-' || str[i] == '*' || str[i] == '/') {  
            if (mystack.empty()) {  
                mystack.push(str[i]);  
            }  
            else {  
                while (!mystack.empty()) {  
                    tmp = mystack.top();  
                    if (getPriority(tmp) >= getPriority(str[i])) {  
                        //彈出棧頂元素  
                        res.push_back(tmp);  
                        mystack.pop();  
                    }  
                    else break;  
                }  
                mystack.push(str[i]);  
            }  
        }  
        else {  
            if(str[i]=='(') mystack.push(str[i]);  
            else {  
                while (mystack.top() != '(') {  
                    tmp = mystack.top();  
                    res.push_back(tmp);  
                    mystack.pop();  
                }  
                mystack.pop();  
            }  
        }  
        i++;  
    }  
  
    //遍歷完后,若棧非空,彈出所有元素  
    while (!mystack.empty()) {  
        tmp = mystack.top();  
        res.push_back(tmp);  
        mystack.pop();  
    }  
    return res;  
}  
  
   
  
int calculator(string str)  
{  
    //計算后綴表達式的值,默認中綴表達式所有數字都是一位的,在0-9之間  
    stack<int> mystack;  
    int size = str.size();  
    int num1, num2, num3;  
    for (int i = 0; i < size; i++) {  
        if (str[i] >= '0' && str[i] <= '9') {  
            mystack.push(str[i] - '0');  
        }  
        else {  
            num2 = mystack.top();  
            mystack.pop();  
            num1 = mystack.top();  
            mystack.pop();  
            if (str[i] == '+') {  
                num3 = num1 + num2;  
            }  
            else if (str[i] == '-') {  
                num3 = num1 - num2;  
            }  
            else if (str[i] == '*') {  
                num3 = num1 * num2;  
            }  
            else if (str[i] == '/') {  
                num3 = num1 / num2;  
            }  
            mystack.push(num3);  
        }  
    }  
    return mystack.top();  
}  
  
   
int main()  
{  
    string str="1+(2-3)*4+4/2";  
    cout <<"中綴表達式為:"<< endl << str << endl;  
    string res = getPostfixExpression(str);  
    cout <<"后綴表達式為:"<< endl << res << endl;  
    int num_res = calculator(res);  
    cout <<"計算結果:"<< endl << num_res << endl;  
    system("pause");  
    return 0;  
}  

三、直接計算中綴表達式
其實將前面的兩步結合起來,就可以得到直接計算的方法。准備一個數字棧和一個符號棧。
從左到右遍歷中綴表達式。如果遇到數字,入數字棧。
如果遇到符號(四個運算符以及括號),跟前面的“中綴表達式轉后綴表達式”過程一樣,對符號棧進行處理。處理過程中,對每一個出棧的運算符:+ - * /,根據“計算后綴表達式”的方法,計算結果(跟數字棧配合)。
如果遍歷完中綴表達式后符號棧還非空,就繼續出符號棧的運算符,計算,直到符號棧為空。最后數字棧剩下的數字就是結果。
下面給出用C++實現“計算中綴表達式”的代碼,里面考慮了“數字不止1位”,並且用浮點型來表示最終運算結果。要求中綴表達式中只能包含整數和運算符(不能包含小數),並且是合法的。

#include<iostream>  
#include<string>  
#include<stack>  
  
using namespace std;  
  
int getPriority(char ch)  
{  
    //獲取優先級  
    if (ch == '(') return 1;  
    else if (ch == '+' || ch == '-') return 2;  
    else if (ch == '*' || ch == '/') return 3;  
    else return 4;  
}  
  
void calculate(stack<double> &mystack, char operation)  
{  
    double num1, num2, num3;  
    num2 = mystack.top();  
    mystack.pop();  
    num1 = mystack.top();  
    mystack.pop();  
    if (operation == '+') {  
        num3 = num1 + num2;  
    }  
    else if (operation == '-') {  
        num3 = num1 - num2;  
    }  
    else if (operation == '*') {  
        num3 = num1 * num2;  
    }  
    else if (operation == '/') {  
        num3 = num1 / num2;  
    }  
  
    mystack.push(num3);  
}  
  
double calculator(string str)  
{  
    //計算中綴表達式,默認輸入是合法的  
    stack<double> mystack_number;  
    stack<char> mystack_operation;  
    int i = 0, j;  
    int size = str.size();  
    char tmp_operation;  
    string tmp_num;  
    while (i < size) {  
        if (str[i] >= '0' && str[i] <= '9') {  
            j = i;  
            while (j < size && str[j] >= '0' && str[j] <= '9') { j++; }  
            tmp_num = str.substr(i, j - i);  
            mystack_number.push(atoi(tmp_num.c_str()));  
            i = j;  
        }  
        else if (str[i] == '+' || str[i] == '-' || str[i] == '*' || str[i] == '/') {  
            if (mystack_operation.empty()) {  
                mystack_operation.push(str[i]);  
            }  
            else {  
                while (!mystack_operation.empty()) {  
                    tmp_operation = mystack_operation.top();  
                    if (getPriority(tmp_operation) >= getPriority(str[i])) {  
                        //計算  
                        calculate(mystack_number, tmp_operation);  
                        mystack_operation.pop();  
                    }  
                    else break;  
                }  
                mystack_operation.push(str[i]);  
            }  
            i++;  
        }  
        else {  
            if (str[i] == '(') mystack_operation.push(str[i]);  
            else {  
                while (mystack_operation.top() != '(') {  
                    tmp_operation = mystack_operation.top();  
                    //計算  
                    calculate(mystack_number, tmp_operation);  
                    mystack_operation.pop();  
                }  
                mystack_operation.pop();  
            }  
            i++;  
        }  
  
    }  
    //遍歷完后,若棧非空,彈出所有元素  
    while (!mystack_operation.empty()) {  
        tmp_operation = mystack_operation.top();  
        //計算  
        calculate(mystack_number, tmp_operation);  
        mystack_operation.pop();  
    }  
    return mystack_number.top();  
}  
  
int main()  
{  
    string str = "1+(2-3)*4+10/2+2*2+2+2/5";  
    cout << "中綴表達式為:" << endl << str << endl;  
    double num_res = calculator(str);  
    cout << "計算結果:" << endl << num_res << endl;  
    system("pause");  
    return 0;  
}  
相信通過這篇文章,大家對這個問題會有所了解。
給出一道思考題:如果加入乘方'^',應該如何處理?要注意,乘方運算是右結合的。
其實很簡單。只有兩處修改:
1)將乘方添加到優先級中:
1:(
2:+ -
3:* /
4:^
5:)
2)在讀中綴表達式的時候,如果讀到乘方^,就將它放進符號棧中。因為乘方的優先級是最高的,而且是右結合的,所以無論它前面出現的是什么運算,這些運算都不能執行。而且它本身能否執行也是不知道的,因此只能進棧。
【參考資料】
https://blog.csdn.net/sinat_27908213/article/details/80273557
2019-02-13
23:09:01
  


免責聲明!

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



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