计算机在计算四则运算表达式时,不能直接计算中缀表达式,而要把表达式转换为逆波兰表达式,然后再进行求解。所以四则运算表达式求解主要分为两个步骤:
- 中缀表达式转换为后缀表达式
- 计算后缀表达式的值
两个步骤都需要借助栈来完成。下面详细介绍两个步骤的实现思路:
1.中缀表达式转换为后缀表达式
核心思想:
假设我们以字符串形式输入中缀表达式,此时我们还需要一个变量convertRes
来储存后缀表达式结果。假设没有空格和制表符等其他额外字符,我们用名为optSymbo的栈来储存运算符,用index来表示当前遍历到的字符位置。
- 遇到数字,直接缀到
convertRes
字符串后面。字符索引index
指向下一个字符 - 遇到左括号
(
,则入栈。字符索引index
指向下一个字符 - 遇到右括号
)
,则栈中必然有左括号,我们不断输出栈顶运算符,直到遇到左括号(
。然后弹出左括号,左括号和右括号都不能出现在后缀表达式中。
注意后缀表达式中运算符前后加空格,便于区分符号和数字
- 遇到
+
或者-
运算符,我们需要查看栈顶是否有优先级大于等于当前+
或者-
的运算符。很明显只要栈顶有+
,-
,*
,/
都需要输出,直到栈为空或者遇到左括号(
。然后当前运算符存入栈中。 - 遇到
*
或者/
运算符,还是要查看栈顶是否有大于等于*
或者/
的运算符,直到遇到左括号或者低于*
或者/
的运算符。然后当前运算符存入栈中。 - 最后字符索引遍历完表达式,查看栈中是否有剩余符号,如果有也一并接到
convertRes
字符串上
2. 计算后缀表达式
计算后缀表达式同样需要借助栈,在这里命名为optNum
。
核心思想:
- 遇到数字,从字符串转换为数字后压栈
- 遇到运算符,提取栈顶的两个数字按照
+
,-
,*
,/
,四种情况做运算,记住第一个操作数是栈顶往下第二个元素,栈顶元素为第一个操作数,计算完成后压栈即可 - 最后输出栈顶元素即为最终结果
3.stringstream字符串类
这个类可以实现将原有字符串按照空格分割,这也是为什么前面提到要在运算符和数字之间加空格。读入后缀表达式,然后遇到空格,就将空格前的字符串装入vector
数组中,便于后续索引和计算。
4. 四则运算代码
// calculatorNEW.cpp
#include<iostream>
#include<stack>
#include<vector>
#include<string>
#include<sstream>
using namespace std;
class Calculator{
public:
int solve(string& s)
{
//0.中缀表达式转后缀表达式
int expressionLen = s.size();
int index = 0;
string convertRes;
stack<char>optSymbol;
while(index < expressionLen)
{
if(s[index] == '+' || s[index] == '-')
{
convertRes += ' ';//数字后面补空格
//当前的加减运算符优先级不高于栈中除(以外的运算符
// while(!optSymbol.empty() && (optSymbol.top() == '+'|| optSymbol.top() == '-' || optSymbol.top() == '*' || optSymbol.top() == '/'))
while(!optSymbol.empty() && optSymbol.top() != '(')
{
convertRes += optSymbol.top();
convertRes += ' ';
optSymbol.pop();
}
optSymbol.push(s[index++]);
}
else if(s[index] == '*' || s[index] == '/')
{
convertRes += ' ';
while(!optSymbol.empty() && (optSymbol.top() == '*' || optSymbol.top() == '/'))
{
convertRes += optSymbol.top();
convertRes +=' ';
optSymbol.pop();
}
optSymbol.push(s[index++]);
}
else if(s[index] == '(')
{
optSymbol.push(s[index++]);
}
else if(s[index] == ')')
{
convertRes += ' ';
while(optSymbol.top() != '(')
{
convertRes += optSymbol.top();
convertRes += ' ';
optSymbol.pop();
}
optSymbol.pop();//弹出左括号
index++;
}
else
{
convertRes += s[index++];
}
}
convertRes += ' ';
while(!optSymbol.empty())
{
convertRes += optSymbol.top();
convertRes += ' ';
optSymbol.pop();
}
stringstream ss(convertRes);
vector<string>result;
string temp;
while(ss >> temp)
{
result.push_back(temp);
}
//1.查看逆波兰表达式是否正确
// for(auto r:result)
// {
// cout << r << " ";
// }
// cout << endl;
//2.计算逆波兰表达式得到结果
index = 0;
stack<int>optNum;
int backExpressionLen = result.size();
while(index < backExpressionLen)
{
string temp2 = result[index];
if(temp2.size() <= 1 && temp2[0] == '+' || temp2[0] == '-' || temp2[0] == '*' || temp2[0] == '/')
{
int num1 = optNum.top();
optNum.pop();
int num2 = optNum.top();
optNum.pop();
switch(temp2[0])
{
case '+':
optNum.push(num2 + num1);
break;
case '-':
optNum.push(num2 - num1);
break;
case '*':
optNum.push(num2 * num1);
break;
case '/':
optNum.push(num2 / num1);
break;
}
}
else
{
optNum.push(stoi(temp2));
}
index++;
}
return optNum.top();
}
};
int main()
{
string s;
getline(cin ,s);
Calculator cal;
cout << cal.solve(s);
system("pause");
return 0;
}