計算中綴表示式
“計算中綴表示式”可以稱得上是一個特別經典的關於棧的演算法題,幾乎在所有資料結構教材中都會涉及,而且很多公司面試或者筆試的時候都會把這道題作為一個考察點。可以說,這是一道必須要掌握的演算法題。中綴表示式、字尾表示式等概念在這裡就不贅述了,讓我們直奔主題。
題目:輸入一箇中綴表示式,計算其結果。
輸入的前提假設:
(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)在讀中綴表示式的時候,如果讀到乘方^,就將它放進符號棧中。因為乘方的優先順序是最高的,而且是右結合的,所以無論它前面出現的是什麼運算,這些運算都不能執行。而且它本身能否執行也是不知道的,因此只能進棧。
大家自己試試吧~要記住,學習程式設計,動手去寫程式碼永遠是第一位的。
【參考資料】
[1]https://blog.csdn.net/sinat_36118270/article/details/70257547
[2]翁惠玉, 俞勇. 資料結構:思想與實現[M]. 高等教育出版社, 2009.
相關文章
- 使用棧結構計算中綴表示式
- 中綴表示式
- js實現四則計算(中綴,字尾表示式)JS
- 中綴表示式轉字尾表示式
- 何謂中綴表示式
- 中綴表示式轉為逆波蘭表示式
- 【資料結構與演算法】中綴表示式轉字尾表示式以及字尾表示式的計算資料結構演算法
- 中綴表示式轉化為字尾表示式並求值
- 關於利用STL棧求解四則中綴表示式以及中綴表示式轉逆波蘭表示式和逆波蘭表示式的求解
- 字首中綴字尾表示式規則
- 資料結構 中綴表示式轉化資料結構
- 逆波蘭演算法、中綴表示式轉字尾表示式演算法
- 教你如何在C++中實現中綴表示式轉字尾表示式C++
- Task A1 中綴表示式轉換為逆波蘭式
- 資料結構與演算法——棧(五)中綴表示式轉字尾表示式資料結構演算法
- 中綴轉字尾表示式思路分析和程式碼實現
- 資訊學奧賽複賽複習09-CSP-J2020-03表示式求值前置知識點-中綴表示式求值、摸運算、模運算性質、棧
- 將算數表示式轉換成字尾表示式並計算結果
- 表示式計算(棧的應用)
- 一個數學表示式的計算
- C語言- 基礎資料結構和演算法 - 09 棧的應用_中綴表示式轉字尾表示式20220611C語言資料結構演算法
- C#字尾表示式解析計算字串公式C#字串公式
- 【計算機演算法】 求字首表示式的值計算機演算法
- 使用棧實現表示式求值,運用棧計算
- leetcode 224. 基本計算器(逆波蘭表示式)LeetCode
- 算數表示式求值--c語言課程設計C語言
- c++虛擬函式實現計算表示式子C++函式
- Kotlin進階(二)中綴、內聯、高階函式Kotlin函式
- 圖解計算機中的資料表示形式圖解計算機
- 計算廣告實現入門-索引布林表示式索引
- C/C++ 陣列連結串列表示式計算C++陣列
- 雲端計算開發教程:Python運算子與表示式Python
- Python 如何傳遞運算表示式Python
- 表示式的動態解析和計算,Flee用起來真香
- 百行以內實現複雜數學表示式計算
- SQLite中的表示式SQLite
- 計算機資料的表示計算機
- 計算機程式的思維邏輯 (88) – 正規表示式 (上)計算機