我们都知道后缀表达式是很好算的,但是如何进行中缀表达式的求值?
如何计算中缀表达式?
一、中缀表达式是什么?
中缀表达式(或中缀记法)是一个通用的算术或逻辑公式表示方法, 操作符是以中缀形式处于操作数的中间(例:3 + 4),中缀表达式是人们常用的算术表示方法。
以上来自百度百科
其实说白了,中缀表达式就是我们日常生活中所用的算式。
比如 1 + 1 * 2 / (1 + 2)就是一个中缀表达式
二、问题分析
为简化问题起见,我们这里仅仅考虑表达式中的数均为非负数,运算包含 + 、- 、 * 、/四种基本运算,另外,表达式中可能出现括号。
1.模拟
我们来考虑一个具体的实例,以计算机的视角来看,我们只能从左向右遍历表达式,对于当前遍历位置之后的表达式我们是不得而知的。
考虑 3 * 5 + 2
从左向右遍历:
- 遇到数字 3,
什么都做不了,先存储- 遇到 *,考虑可能要进行乘法,依然不能确定后边有无括号,先存储
- 遇到 5,这时候可以直接乘吗?还是 先存储
- 遇到 +,考虑前一个运算是乘法,这时我们可以笃定上一个运算一定是 3 * 5
- 如果当前位是 * 或者 /,我们依然有上面的结论
由此我们得到了第一个结论
结论1 : 遍历到一个运算符时,如果上一个运算符(与当前运算符间无括号)的优先级大于等于当前运算符,那么在表达式里,我们一定先算上一个运算符
2.有括号的情况
考虑 3 * (5 + 2)
从左向右遍历:
- 遇到数字 3,
什么都做不了,先存储- 遇到 *,考虑可能要进行乘法,依然不能确定后边有无括号,先存储
- 遇到 (,后面一定有一个有括号和它对应,先存储
- 遇到 5,说明它是括号表达式的一部分,还是 先存储
- 遇到 +,前一个运算是乘法,这时我们可以笃定上一个运算一定是 3 * 5吗???不行,因为这里多出来一个拦路虎 (
- 我们可以看出,括号表达式相当于把原表达式截取了一部分,这一部分和其他部分是相互独立的,因此,我们在遍历到一个右括号时,要把 上一个出现的左括号和它之间的部分取出来进行计算!!!
于是我们得到了结论2
结论2 : 当前位为左括号,直接储存等待和右括号配对;当前位为右括号,读取到上一个左括号之后的所有运算符和数字进行计算
到这里,聪明的读者已经发现,我们应该使用的数据结构为 : 栈
具体实现思路如下:
中缀表达式求值
方法:开两个栈,操作符栈和数组栈
从左到右扫描中缀表达式
- 当前位为数字:继续向后扫描直到读取完整个数字,将其放入数字栈中
- 当前位为左括号,直接放入操作符栈中等待和右括号配对
- 当前位为右括号,从操作符栈中读取到上一个左括号之后的所有运算符,
每读取一个就取数字栈中两个元素进行计算,并把结果放入栈中- 当前位为运算符,如果栈顶元素优先级更高或者和当前运算符相等,就弹出
栈顶操作符,并进行计算,知道栈顶元素为左括号或者栈顶<当前或者栈为空
遍历结束表达式后,将剩余的操作符进行计算
三、C++实现
#include<iostream>
using namespace std;
char op[100010]; //stack of operators
long long nums[100010]; //stack of numbers
int t1 = -1,t2 = -1;
bool isdigit(char ch)
{
return ch >= '0' && ch <= '9';
}
int compare(char a, char b)
{
if((a == '+' || a == '-') && (b == '*' || b ==
'/'))return -1;
else if((b == '+' || b == '-') && (a == '*' || a ==
'/'))return 1;
return 0;
}
void calc(char ch)
{
long long b = nums[t2--], a = nums[t2 --];
if(ch == '+') nums[++t2] = a + b;
else if(ch == '-')nums[++t2] = a - b;
else if(ch == '*')nums[++t2] = a * b;
else if(ch == '/')nums[++t2] = a / b;
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
string s;
cin >> s;
for(int i = 0;i < s.size();i ++){
char x = s[i];
if(isdigit(x)){
long long num = x - '0';
while(i < s.size() && isdigit(s[++i]))num = num * 10 + s[i] - '0';
i--;
nums[++ t2] = num;
}
else if(x == '(')op[++ t1] = x;
else if(x == ')'){
char ch = op[t1--];
while(ch == '+' || ch == '*' || ch == '-' || ch == '/'){
calc(ch);
ch = op[t1--];
}
}
else{
char ch = op[t1];
if(ch == '('){
op[++ t1] = x;
continue;
}
while(t1 != -1 && ch != '(' && compare(x,ch) != 1 )
{
t1--;
calc(ch);
ch = op[t1];
if(ch == '('){
break;
}
}
op[++ t1] = x;
}
}
while(t1 != -1)
{
char ch = op[t1 --];
calc(ch);
}
cout << nums[t2];
return 0;
}
由于每个元素最多入栈出栈一次,因此时间复杂度为 O(n)
空间复杂度为 O(n)
总结
限于笔者水平,如有纰漏还请见谅。