这道题是 逆波兰表达式求值的一个延伸。
在解上述这道题时,明显在后缀表达式的计算中,不用考虑运算符优先级的问题。程序运行时间跟问题规模是线性关系,即时间复杂度是O(N)。而在下面这道题中,要实现简单计算器的功能,则要考虑运算符的优先级的问题,还有着运算结合顺序的问题。
题目描述
给定一个包含正整数、加(+)、减(-)、乘()、除(/)的算数表达式(括号除外),计算其结果。
表达式仅包含非负整数,+, - ,,/ 四种运算符和空格 。 整数除法仅保留整数部分。
示例 1:
输入: “3+2*2” 输出: 7
示例 2:
输入: " 3/2 " 输出: 1
示例 3:
输入: " 3+5 / 2 " 输出: 5
说明:
你可以假设所给定的表达式都是有效的。请不要使用内置的库函数 eval。
题解(不考虑有括号的情况)
思路:先将中缀表达式 转换为后缀表达式,然后再利用后缀表达式求值。
问题就困难在 如何将中缀表达式转换为后缀表达式?
- 通过举例可以发现 转换前和转换后的表达式中 运算数的相对顺序没有变,改变的是操作符的相对顺序
- 想到用 栈 来存储暂时等待中的运算符,将当前的运算符与栈顶的运算符做比较,来决定弹出还是压入
步骤: - 从头到尾读取中缀表达式的每个对象,对不同对象按不同的情况进行处理
- 因此需要用到一个数组来存储转换后的后缀表达式,用一个栈来存储运算符
- 运算数:直接插入结果数组中,其后加
' '
,这里其实要注意使用while循环
读入运算数,因为运算数有不是1位数的情况,读入完整运算数后再在其后加空格 。同样地 在ecalculateRPN
函数模块中读取运算数时也应该注意,使用while循环
将字符转换为整型 - 运算符:如果当前栈为空,则直接压入栈中,否则要比较当前运算符与栈顶运算符的优先级
- 如果优先级
>
栈顶运算符 压入栈中 - 如果优先级
<=
栈顶运算符 将栈顶运算符弹出并输出到结果数组中,其后加' '
。再比较新的栈顶运算符 直到该运算符优先级>
栈顶运算符为止然后将该运算符压入栈。 - 如果各对象处理完毕,则把栈中的运算符一一输出到结果数组中,其后加
' '
。 - 在结果数组中加空格的目的是 将 运算数和 运算符隔开,方便在
ecalculateRPN
模块中对运算数和运算符的判定添加便利
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <cstdlib>
#include <cstring>
using namespace std;
int calculate(string s);
vector<char> toRPN(string s);//注意这里 是把后缀表达式存储为 char类型的vector数组中,不再是Leetcode150题中的string 类型。方便后续比较不同运算符中的优先级 (用swtich语句)
int checkPriority(char ch);
int calculateRPN(vector<char> tokens);
int main()
{
string s;
getline(cin,s);
int ans=calculate(s);
cout << ans <<endl;
return 0;
}
int calculate(string s) {
vector<char>later;
later=toRPN(s);
for(int i=0;i<later.size();i++)
{
cout << later[i];
}
cout <<endl;
return calculateRPN(later);
}
int checkPriority(char ch)
{
switch(ch)
{
case '+': return 1;
case '-': return 1;
case '*': return 2;
case '/': return 2;
default: return 0;
}
}
vector<char> toRPN(string s)
{
vector<char> s1; //存储转换后的后缀表达式
stack<char> op; //存储等待中的运算符号
for(int i = 0; i < s.size(); ++i)
{
//输入的字符串中可能包含空格 要跳过
if(s[i] == ' ') continue;
//如果遇到的是运算符 则需要判断它的优先级 与 当前栈顶的运算符的优先级
if(s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/')
{
if(op.empty())
op.push(s[i]);
else
{
if(checkPriority(s[i]) > checkPriority(op.top()))
op.push(s[i]);
else
{
while(!op.empty() && (checkPriority(s[i]) <= checkPriority(op.top())))
{
s1.push_back(op.top());
s1.push_back(' ');
op.pop();
}
op.push(s[i]);
}
}
}
else if(s[i]>='0' && s[i]<='9')//如果遇到运算数直接放进 s1
{
while(s[i]>='0' && s[i]<='9')
{
s1.push_back(s[i]);
++i;
}
s1.push_back(' ');
--i;
}
}
while(!op.empty())
{
s1.push_back(op.top());
s1.push_back(' ');
op.pop();
}
return s1;
}
int ecalculateRPN(vector<char> tokens)
{
stack<int> data;
int sz = tokens.size();
for(int i = 0; i < sz; ++i){
if(tokens[i]=='+'){
int b = data.top();
data.pop();
int a = data.top();
data.pop();
data.push(a+b);
}
else if(tokens[i]=='-'){
int b = data.top();
data.pop();
int a = data.top();
data.pop();
data.push(a-b);
}
else if(tokens[i]=='*'){
int b = data.top();
data.pop();
int a = data.top();
data.pop();
data.push(a*b);
}
else if(tokens[i]=='/'){
int b = data.top();
data.pop();
int a = data.top();
data.pop();
data.push(a/b);
}
else if(tokens[i]>='0' && tokens[i]<='9')
{
int tmp=tokens[i]-'0';
int j=++i;
while(tokens[j]>='0' && tokens[j]<='9')
{
tmp=tmp*10+(tokens[j]-'0');//要加括号 不然可能会越界
++j;
}
data.push(tmp);
i=--j;
}
}
return data.top();
}
解法:有括号怎么办呢?
其实就是在中缀表达式 转后缀表达式的过程中增加对括号的判定:
- 左括号:直接放入存储操作数的栈中
- 右括号:将此时栈顶的操作符弹出并输出到结果数组中,直到遇到左括号(即:右括号不入栈,一直弹出栈顶的运算符,知道遇到左括号,弹出但不输出)
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <cstdlib>
#include <cstring>
using namespace std;
int calculate(string s);
vector<char> toRPN(string s);
int checkPriority(char ch);
int calculateRPN(vector<char> tokens);
int main()
{
string s;
getline(cin,s);
int ans=calculate(s);
cout << ans <<endl;
return 0;
}
int calculate(string s) {
vector<char>later;
later=toRPN(s);
for(int i=0;i<later.size();i++)
{
cout << later[i];
}
cout <<endl;
return calculateRPN(later);
}
int checkPriority(char ch)
{
switch(ch)
{
case '+': return 1;
case '-': return 1;
case '*': return 2;
case '/': return 2;
default: return 0;
}
}
vector<char> toRPN(string s)
{
vector<char> s1; //存储转换后的后缀表达式
stack<char> op; //存储等待中的运算符号
for(int i = 0; i < s.size(); ++i)
{
//如果遇到空格 跳过
if(s[i] == ' ') continue;
if(s[i] == '(')
{
op.push(s[i]);
}
if(s[i] == ')')
{
while(!op.empty() && op.top()!='(')
{
s1.push_back(op.top());
s1.push_back(' ');
op.pop();
}
op.pop();//将栈顶的左括号弹出
}
//如果遇到的是运算符 则需要判断它的优先级 与 当前栈顶的运算符的优先级
if(s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/')
{
if(op.empty())
op.push(s[i]);
else
{
if(checkPriority(s[i]) > checkPriority(op.top()))
op.push(s[i]);
else
{
while(!op.empty() && (checkPriority(s[i]) <= checkPriority(op.top())))
{
s1.push_back(op.top());
s1.push_back(' ');
op.pop();
}
op.push(s[i]);
}
}
}
else if(s[i]>='0' && s[i]<='9')//如果遇到运算数直接放进 s1
{
while(s[i]>='0' && s[i]<='9')
{
s1.push_back(s[i]);
++i;
}
s1.push_back(' ');
--i;
}
}
while(!op.empty())
{
s1.push_back(op.top());
s1.push_back(' ');
op.pop();
}
return s1;
}
int calculateRPN(vector<char> tokens)
{
stack<int> data;
int sz = tokens.size();
for(int i = 0; i < sz; ++i){
if(tokens[i]=='+'){
int b = data.top();
data.pop();
int a = data.top();
data.pop();
data.push(a+b);
}
else if(tokens[i]=='-'){
int b = data.top();
data.pop();
int a = data.top();
data.pop();
data.push(a-b);
}
else if(tokens[i]=='*'){
int b = data.top();
data.pop();
int a = data.top();
data.pop();
data.push(a*b);
}
else if(tokens[i]=='/'){
int b = data.top();
data.pop();
int a = data.top();
data.pop();
data.push(a/b);
}
else if(tokens[i]>='0' && tokens[i]<='9')
{
int tmp=tokens[i]-'0';
int j=++i;
while(tokens[j]>='0' && tokens[j]<='9')
{
tmp=tmp*10+(tokens[j]-'0');//要加括号 不然可能会越界
++j;
}
data.push(tmp);
i=--j;
}
}
return data.top();
}