题目链接
自己的解题思路(略显麻烦)
准备两个堆栈分别存放运算符(op)和待操作的数字(number)。先将表达式以string类型保存到str变量里,再以空格为间隔符,把多个str的子串存在string数组arr中,遍历数组arr。
若元素是一个数字:op的头部是*或者/运算符的话,就执行该运算操作;反之,则把该元素添加到number堆栈中
若元素是一个运算符:若op不为空且该元素的level为1,就先执行op头部的运算操作,再将该元素添加到op堆栈中。反之则直接添加。
总结一下,就是自己也觉得代码越写越乱。不断查测试用例,然后不断改bug,这样的做题方式是不对的。最关键的是,我一开始没认识到,如果多个同level的运算符相连,是按从左到右的方式运算;而如果是根据堆栈弹出的顺序,则等价于从右到左的顺序,显然这不能产生正确的题解。
#include <iostream>
#include <stack>
#include <cstdio>
using namespace std;
float Tran(string x) {
float ans=0;
for(int i=0; i<x.size(); i++) {
ans*=10;
ans+=(x[i]-'0');
}
return ans;
}
bool isNum(string x) {
if(x[0]>='0' && x[0]<='9')
return true;
return false;
}
int getLevel(string x) {
if(x=="+" || x=="-")
return 1;
return 2;
}
float funCal(float a, float b, string op) {
if(op=="+")
return a+b;
else if(op=="-")
return a-b;
else if(op=="*")
return a*b;
else
return a/b;
}
int main() {
string str;
while(getline(cin,str) && str!="0") {
stack<float> number;
stack<string> op;
string arr[201];
int pre=0,len=str.size(),arrlen=0;
for(int i=0; i<len; i++) {
if(str[i]==' ') {
arr[arrlen++]=str.substr(pre, i-pre);
pre=i+1;
}
}
arr[arrlen++]=str.substr(pre);
for(int i=0; i<arrlen; i++) {
if(isNum(arr[i])) {
if(op.size()>0 && (op.top()=="*" || op.top()=="/")) {
float a = number.top();
number.pop();
number.push(funCal(a,Tran(arr[i]),op.top()));
op.pop();
} else
number.push(Tran(arr[i]));
} else {
if(op.size()>0 && getLevel(arr[i])==1) {
float b=number.top();
number.pop();
float a=number.top();
number.pop();
number.push(funCal(a,b,op.top()));
op.pop();
}
op.push(arr[i]);
}
}
while(number.size()>1) {
float b=number.top();
number.pop();
float a=number.top();
number.pop();
number.push(funCal(a,b,op.top()));
op.pop();
}
printf("%.2f\n",number.top());
}
return 0;
}
比较清晰的思路
- 设立运算符和运算数两个栈,一个用来存储运算符,另一个用来存储运算数。
- 在运算符栈中放置一个特殊运算符#其优先级最低。
- 将表达式尾部添加一个特殊运算符$,其优先级次低。
- 从左到右依次遍历字符串,若遍历到运算符,则将其与运算符栈的栈顶元素进行比较,若运算符栈的栈顶的优先级小于该运算符,则将该运算符压入运算符栈;若运算符栈的栈顶的优先级大于等于该运算符,则弹出该栈顶运算符,从运算数栈中依次弹出运算数,完成弹出运算符对应的运算后,再将该结果压入运算数栈。
- 若遍历到表达式中的运算数,则直接压入运算数栈。
- 若运算符栈中仅剩两个特殊运算符#和$,则表达式运算结束,此时运算数栈中唯一的数字就是表达式的值。
将运算符栈栈底的特殊运算符#的优先级设为最低,于是后续遇到的任何运算符的优先级都会比它高,能够顺利入栈。
将表达式尾部的特殊运算符$的优先级设为次低,于是当遍历到表达式尾部时,若运算符栈中尚有可以运算的符号,则可继续运算;若运算符栈中只剩下特殊符号#,则也可保证$能够顺利入栈,并且可以让程序正确地退出。
isdigit()函数在头文件中cctype中,该函数能够用来检查字符是否为十进制数字字符。
#include <iostream>
#include <cstdio>
#include <cctype>
#include <string>
#include <stack>
using namespace std;
int Priority(char c){
if(c=='#') return 0;
else if (c=='$') return 1;
else if(c=='+'||c=='-') return 2;
else return 3;
}
double GetNumber(string str,int& index){
double number = 0;
while(isdigit(str[index])){
number = number*10+str[index++]-'0';
}
return number;
}
double Calculate(double x,double y,char op){
if(op=='+') return x+y;
else if(op=='-') return x-y;
else if(op=='*') return x*y;
else return x/y;
}
int main(){
string str;
while(getline(cin,str) && str!="0"){
int index=0; // 字符串下标
stack<char> oper; // 运算符栈
stack<double> data; // 运算数栈
str+='$'; // 字符串尾部添加$
oper.push('#'); // 运算符栈底添加#
while(index<str.size()){
if(str[index]== ' ') index++;
else if(isdigit(str[index])) data.push(GetNumber(str, index));
else{
if(Priority(oper.top())<Priority(str[index])){
oper.push(str[index++]);
}else{
double y=data.top();
data.pop();
double x=data.top();
data.pop();
data.push(Calculate(x,y,oper.top()));
oper.pop();
}
}
}
printf("%.2lf\n",data.top());
}
return 0;
}