0 问题描述
输入一个只包含个位数字的简单四则运算表达式字符串,计算该表达式的值
注:
- 表达式只含 +, -, *, /, (, ), 四则运算符
- 表达式数值只包含个位整数(0-9),且不会出现0作为除数的情况
- 要考虑加减乘除按通常四则运算规定的计算优先级
- 除法用整数除法,即仅保留除法运算结果的整数部分。比如8/3=2。输入表达式保证无0作为除数情况发生
- 输入字符串一定是符合题意合法的表达式,其中只包括数字字符和四则运算符字符,除此之外不含其它任何字符,不会出现计算溢出情况
• 要求实现函数:
int calculate(int len,char *expStr)
//int len: 字符串长度;
//char *expStr: 表达式字符串;
//返回计算结果
一、后缀表达式
1.原理
关于解法之前在网上查过,但是都过于复杂,看的没有头绪。后来在程杰著的《大话数据结构》一书的第四章——栈的应用中看到了关于四则表达式求值的问题·,其中提到了后缀表达式,即所有的符号都是在要运算的数字后面出现。例如我们要计算
9 + ( 3 - 1 ) × 3 + 10 ÷ 2
将其转换为后缀表达式即为
9 3 1 - 3 × + 10 ÷ 2
那么怎样由后缀表达式求得最终运算结果呢,书中也给了详细介绍:
具体步骤上面说的很详细,不过简而一句话就是从左到右遍历字符串,遇到数字则进栈,遇到运算符 则将栈顶两个数字取出,进行相应的运算,然后再将结果存入栈中,最后得到的就是表达式的结果
2.代码
int Solution::calculate(int len, char *expStr)
{
string str = expStr;
stack<int> num; //运算的结果不止一位数 类型要用int而不是char
int num1 = 0, num2 = 0, result = 0; //操作数1,操作数2,结果
for (int i = 0; i < str.length(); i++) //遍历字符串
{
if (str[i] <= '9' && str[i] >= '0') //如果是数字则进栈
{
num.push(str[i] - '0'); //由ASCII码转换为数字
}
else //遇到运算符
{
num2 = num.top(); //先将栈顶取出 再pop()
num.pop();
num1 = num.top();
num.pop();
switch (str[i]) //判断运算符,并进行相应运算,结果再存入栈中
{
case '+':
result = num1 + num2;
num.push(result);
break;
case '-':
result = num1 - num2;
num.push(result);
break;
case '*':
result = num1 * num2;
num.push(result);
break;
case '/':
result = num1 / num2;
num.push(result);
break;
default:
break;
}
}
}
return result; //返回结果
}
二、中缀表达式转后缀表达式
对于我们常用的中缀表达式如何转换为后缀表达式,书中也给了详细的说明:
规则:从左到右遍历中缀表达式中的每一个数字和符号,如果是数字就输出,即成为后缀表达式的一部分;如果是符号,则判断其与栈顶符号的优先级,是右括号或者优先级不高于栈顶符号,则栈顶元素依次输出并出栈,然后将当前符号进栈,直到最终输出后缀表达式为止。
代码:
string Solution::mid2suff(int len,char *expStr)
{
string str = expStr;
int n = 0;
list<char> suffixExp; //用于存放输出的后缀表达式
list<char>::iterator it; //用于遍历后缀表达式的list
stack<char> tmp; //用于符号进栈
for (int i = 0; i < len; i++)
{
if (str[i] <= '9' && str[i] >= '0') //是数字就输出
{
suffixExp.push_back(str[i]);
}
else //如果是符号就判断该符号与栈顶符号的优先级
{
if (tmp.empty()) //如果是空栈 则符号直接进栈
{
tmp.push(str[i]);
}
else //不是空栈 则与栈顶比较
{
switch (str[i])
{
case '+': //如果是+- ,优先级最低 原来的栈顶先出栈 然后 + - 进栈
case '-':
while ((!tmp.empty()) && (tmp.top() != '(')) //'('只有遇到')'才输出
{
suffixExp.push_back(tmp.top()); //要先输出到后缀表达式 再出栈
tmp.pop();
}
tmp.push(str[i]); //+-进栈
break;
case '(': //遇到'('直接进栈
tmp.push(str[i]);
break;
case ')': //如果是 ) 那么括号之间的符号出栈 (加入到输出)
while (tmp.top() != '(')
{
suffixExp.push_back(tmp.top());
tmp.pop();
}
tmp.pop(); //将 左括号 出栈 但是不加入到输出中
break;
case '*':
case '/':
if(tmp.empty()) //如果空栈 直接进栈
tmp.push(str[i]);
else //遇到 */ 时 前面的*或者/要先出栈
{
while ((tmp.top() =='*')||(tmp.top() == '/'))
{
suffixExp.push_back(tmp.top());
tmp.pop();
if (tmp.empty())
break;
}
tmp.push(str[i]); // * / 进栈
}
break;
default:
break;
}
}
}
}
while (!tmp.empty()) //表达式遍历结束 如果栈中还有符号则依次出栈
{
suffixExp.push_back(tmp.top());
tmp.pop();
}
for (it = suffixExp.begin();it!=suffixExp.end();it++) // 将list转为string
{
str[n++] = *it;
}
if (n < len) //string的末尾删除
{
str.erase(n);
}
//cout << str << endl;
return str;
}
其中需要注意的地方:
1.如果栈区为空时 执行 pop()操作程序会出错,故在栈区有可能为空的情况下,出战之前要先判断stack.empty();
2.同样,在栈区为空时,查询栈顶数据也会出错,程序中有一段代码:
while ((!tmp.empty()) && (tmp.top() != '(')) //'('只有遇到')'才输出
{
suffixExp.push_back(tmp.top()); //要先输出到后缀表达式 再出栈
tmp.pop();
}
如果while的两个判断条件交换顺序,则空栈的时候该处会出错;
3.上面的规则中,“输出”指的是将栈顶元素输出到后缀表达式中,而“出栈”仅仅是将栈顶元素删除,故应先将栈顶元素放到后缀表达式中再执行出栈操作。