简单计算器 - 九度教程第27题
题目:
时间限制:1 秒 内存限制:32 兆 特殊判题:否
题目描述:
读入一个只包含 +, -, *, / 的非负整数计算表达式,计算该表达式的值。
输入:
测试输入包含若干测试用例,每个测试用例占一行,每行不超过200个字符,整数和运算符之间用一个空格分隔。没有非法表达式。当一行中只有0时输入结
束,相应的结果不要输出。
输出:
对每个测试用例输出1行,即该表达式的值,精确到小数点后2位。
样例输入:
1 + 2
4 + 2 * 5 - 7 / 11
0
样例输出:
3.00
13.36
来源:
2006年浙江大学计算机及软件工程研究生机试真题
解析:
利用堆栈对表达式求值的方法:
1.设立两个堆栈,一个用来保存运算符,另一个用来保存数字。
2.在表达式首尾添加标记运算符,该运算符运算优先级最低。
3.从左至右依次遍历字符串,若遍历到运算符,则将其与运算符栈的栈顶元素进行比较,若运算符栈的栈顶运算符优先级小于该运算符或者此时运算符栈为空,则将该运算符压入堆栈。遍历字符串中下一个元素。
4.若运算符栈的栈顶运算符优先级大于该运算符,则弹出该栈顶运算符,再从数字栈中依次弹出两个栈顶数字,完成弹出的运算符对应的运算并得到结果后,再将该结果压入数字栈,重复比较此时栈顶运算符与当前遍历到的运算符优先级,视其优先级大小重复步骤3或步骤4。
5.若遍历到表达式中的数字,则直接压入数字栈。
6.若运算符堆栈中仅存有两个运算符且栈顶元素为我们人为添加的标记运算符,那么表达式运算结束,此时数字堆栈中唯一的数字即为表达式的值。
代码:
#include<stdio.h>
#include<stack>
using namespace std;
char str[201]; //保存表达式字符串
int mat[][5]={ //优先级矩阵,若mat[i][j]==1,则表示i号运算符优先级大于j号运算符,
//运算符编码规则为+为1号,-为2号,*为3号,/为4号,
//我们人为添加在表达式首尾的标记运算符为0号
1,0,0,0,0,
1,0,0,0,0,
1,0,0,0,0,
1,1,1,0,0,
1,1,1,0,0,
};
stack<int> op; //运算符栈,保存运算符号
stack<double> in;//数字栈,运算结果可能存在浮点数,所以保存元素为double
void getOp(bool &reto,int &retn,int &i)
{
//获得表达式中下一个元素函数,若函数运行结束时,引用变量reto为true,则表示该元素为一个运算符,其编号保存在引用变量retn中;
//否则,表示该元素为一个数字,其值保存在引用变量retn中,引用变量i表示遍历到的字符串下标
if(i==0&&op.empty()==true)
{
//若此时遍历字符串第一个字符,且运算符栈为空,我们认为添加编号为0的标记字符
reto=true; //为运算符
retn=0; //编号为0
return; //返回
}
if(str[i]==0) //若此时遍历字符串为空字符,则表示字符串已经被遍历完
{
reto=true; //返回为运算符
retn=0; //编号为0的标记字符
return; //返回
}
if(str[i]>='0' && str[i]<='9')//若当前字符为数字
{
reto=false; //返回为数字
retn=0; //返回结果为数字
for(;str[i]!=' ' && str[i]!=0;i++)
{ //若字符串未被遍历完,且下一个字符不是空格,则依次遍历其后数字,计算当前连续数字字符表示的数值
retn*=10;
retn+=str[i]-'0';
} //计算该数字的数字值
if(str[i]=' ')//若其后字符为空格,则表示字符串未被遍历完
{
i++; //i递增,跳过该空格
return;
}
}
else //否则
{
reto=true; //返回为运算符
if(str[i]=='+')//加号返回1
{
retn=1;
}
else if(str[i]=='-') //减号返回2
{
retn=2;
}
else if(str[i]=='*') //乘号返回3
{
retn=3;
}
else //除号返回4 if(str[i]=='/')
{
retn=4;
}
i+=2; //i递增,跳过该运算字符和该运算字符后的空格
return; //返回
}
return; //返回
}
int main()
{
while(gets(str))//输入字符串,当其位于文件尾时,gets返回0
{
if(str[0]=='0'&&str[1]==0) break;//若输入只有一个0,则退出
bool retop;
int retnum; //定义函数所需的引用变量
int idx=0; //定义遍历到的字符串下标,初始值为0
while(!op.empty()) op.pop();
while(!in.empty()) in.pop();//清空数字栈,和运算符栈
while(true){ //循环遍历表达式字符串
getOp(retop,retnum,idx);//获取表达式中下一个元素
if(retop==false) //若该元素为数字
{
in.push((double)retnum);//将其压入数字栈中
}else{
double tmp;
if(op.empty()==true || mat[retnum][op.top()]==1)
{
op.push(retnum);
}else{ //若运算符堆栈为空或者当前遍历到的运算符优先级大于栈顶元素,将该运算符压入运算符栈
while(mat[retnum][op.top()]==0)//!!!!!这个地方是==,不是=
{
//只要当前运算符优先级小于栈顶元素运算符,则重复循环
int ret=op.top();//保存栈顶运算符
op.pop(); //弹出
double b=in.top();
in.pop();
double a=in.top();
in.pop(); //从数字堆栈栈顶弹出两个数字,依次保存在遍历a、b中
if(ret==1)tmp=a+b;
else if(ret==2)tmp=a-b;
else if(ret==3)tmp=a*b;
else tmp=a/b;
in.push(tmp); //将结果压回数字栈
}
op.push(retnum); //将当前运算符压入运算符堆栈
}
}
if(op.size()==2 && op.top()==0)break;
//若运算符堆栈只有两个元素,且其栈顶元素为标记运算符,则表示表达式求值结束
}
printf("%.2f\n",in.top()); //输出数字栈中唯一的数字,即为答案
}
return 0;
}
while(mat[retnum][op.top()]==0) //!!!!!这个地方是==,不是=
找错误的时候,找了半天,才发现是这错了!!!要细心呐!!!吐血......