計算中綴表達式」能夠稱得上是一個特別經典的關於棧的算法題,幾乎在全部數據結構教材中都會涉及,並且不少公司面試或者筆試的時候都會把這道題做爲一個考察點。能夠說,這是一道必需要掌握的算法題。中綴表達式、後綴表達式等概念在這裏就不贅述了,讓咱們直奔主題。
題目:輸入一箇中綴表達式,計算其結果。
輸入的前提假設:
(1)只考慮+、-、*、/這四種運算符,中綴表達式中只有一種括號:();
(2)輸入的中綴表達式中只有整數,沒有小數;
(3)假定輸入是合法的。
不少文章或課本喜歡一步到位,直接討論如何從中綴表達式計算結果。但對於初學者來講,跨度未免大了點。這裏按部就班,從易到難,先討論如何將中綴表達式轉化爲後綴表達式,再討論如何計算後綴表達式。最後在前面兩步的基礎上,討論如何一步到位,直接計算中綴表達式的結果:
1、如何將中綴表達式轉化爲後綴表達式
在平常應用中,算術表達式中運算符老是出如今兩個操做數之間,例如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)字符串遍歷結束後,若是棧不爲空,則彈出棧中全部元素,將它們添加到後綴表達式的末尾,直到棧爲空。
2、計算後綴表達式
後綴表達式的計算就至關簡單了。準備一個數字棧。從左到右掃描後綴表達式,若是是數字,放入數字棧。若是是符號,從數字棧中彈出兩個數字,第一個取出的數字爲右運算數,第二個爲左運算數,進行運算。而後將結果放進數字棧中。如此反覆,直到讀完整個表達式後,留在數字棧中的那個數字就是最終結果。
C++代碼以下,要注意,下面的代碼默認中綴表達式中全部數字都是整數,而且都在0到9之間。並且計算結果都是整數(好比5/2=2)。ios
#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; }
3、直接計算中綴表達式
其實將前面的兩步結合起來,就能夠獲得直接計算的方法。準備一個數字棧和一個符號棧。
從左到右遍歷中綴表達式。若是遇到數字,入數字棧。
若是遇到符號(四個運算符以及括號),跟前面的「中綴表達式轉後綴表達式」過程同樣,對符號棧進行處理。處理過程當中,對每個出棧的運算符:+ - * /,根據「計算後綴表達式」的方法,計算結果(跟數字棧配合)。
若是遍歷完中綴表達式後符號棧還非空,就繼續出符號棧的運算符,計算,直到符號棧爲空。最後數字棧剩下的數字就是結果。
下面給出用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; }