简单计算器
题目描述
读入一个只包含 +, -, *, / 的非负整数计算表达式,计算该表达式的值。
输入
测试输入包含若干测试用例,每个测试用例占一行,每行不超过200个字符,整数和运算符之间用一个空格分隔。没有非法表达式。当一行中只有0时输入结束,相应的结果不要输出。
输出
对每个测试用例输出1行,即该表达式的值,精确到小数点后2位。
样例输入 Copy
30 / 90 - 26 + 97 - 5 - 6 - 13 / 88 * 6 + 51 / 29 + 79 * 87 + 57 * 92
0
样例输出 Copy
-代码1:不能计算负号
//北航机试2020/3/27
#include<iostream>
#include<stdio.h>
#include<stack>
#include<queue>
#include<string>
#include<map>
using namespace std;
/*
转化为后缀
后缀计算
*/
struct node
{
double num;
char op;
bool flag;
};
string str;
stack<node> s;
queue<node> q;
map<char,int> op;
//转化为后缀
void Change()
{
int i;
//首先应该定义优先级
//然后处理不同的字符(数字、运算符)
double num;
node temp;
for(i = 0;i<str.length();)
{
if(str[i]>='0' && str[i] <= '9')//数字
{
temp.flag = true;
temp.num = str[i] - '0';//*****************
i++;//**************************
while(i<str.length()&&str[i]>='0'&& str[i]<='9')
{
temp.num =temp.num*10 + (str[i] - '0');
i++;
}
q.push(temp);
}
else//操作符
{
temp.flag = false;
while(!s.empty() && op[str[i]] <= op[s.top().op])
{
q.push(s.top());
s.pop();
}
temp.op = str[i];
s.push(temp);
i++;
}
}
while(!s.empty())
{
q.push(s.top());
s.pop();
}
}
double cal()
{
double temp1,temp2;
node cur,temp;//cur保存当前的队首元素、temp将两个操作符的结果保存
while(!q.empty())
{
cur = q.front();
q.pop();
if(cur.flag == true)
{
s.push(cur);
}
else
{
temp2 = s.top().num;
s.pop();
temp1 = s.top().num;
s.pop();
temp.flag = true;
if(cur.op == '-') temp.num = temp1-temp2;
else if(cur.op == '+') temp.num = temp1 + temp2;
else if(cur.op == '*') temp.num = temp1 * temp2;
else temp.num = temp1/temp2;
s.push(temp);
}
}
return s.top().num;
}
int main()
{
op['+'] = op['-'] = 1;
op['*'] = op['/'] = 2;
while(getline(cin,str),str!="0")
{
/*
//去除所有的空格,注意没有使用size(),大小会发生变化
for(string::iterator it = str.begin();it != str.end();it++)//***********
{
if(*it == ' ') str.erase(it);
}
*/
for(string::iterator it = str.end();it != str.begin();it--)//***********
{
if(*it == ' ') str.erase(it);
}
//cout<<str<<endl;
while(!s.empty()) s.pop();//初始化栈
Change();
printf("%.2f\n",cal());
}
return 0;
}
- 代码2:能够计算负号(如:-4-8*2)
//只需要把上面的cal()函数替换成下面的即可
首先需要判断的是什么情况下回进行负运算?
当碰到运算符为-
时会有两种情况 - 第一种就是正常的减法运算,特征是此时的栈中的操作数>1
- 第二种就是负运算,特征是栈中的操作数只有一个
double cal()
{
//能够运算负数
double temp1,temp2;
node cur,temp;//cur保存当前的队首元素、temp将两个操作符的结果保存
while(!q.empty())
{
cur = q.front();
q.pop();
if(cur.flag == true)
{
s.push(cur);
}
else
{
if(cur.op == '-')
{
if(s.size() == 1)
{
temp.num = 0-s.top().num;
s.pop();
}
else
{
temp2 = s.top().num;
s.pop();
temp1 = s.top().num;
s.pop();
temp.num = temp1-temp2;
}
}
else
{
temp2 = s.top().num;
s.pop();
temp1 = s.top().num;
s.pop();
if(cur.op == '+') temp.num = temp1 + temp2;
else if(cur.op == '*') temp.num = temp1 * temp2;
else temp.num = temp1/temp2;
}
temp.flag = 1;
s.push(temp);
}
}
return s.top().num;
}
- 关于中缀表达式的计算
- 计算中缀表达式”可以称得上是一个特别经典的关于栈的算法题,几乎在所有数据结构教材中都会涉及,而且很多公司面试或者笔试的时候都会把这道题作为一个考察点。可以说,这是一道必须要掌握的算法题。中缀表达式、后缀表达式等概念在这里就不赘述了,让我们直奔主题。
题目:输入一个中缀表达式,计算其结果。
输入的前提假设:
(1)只考虑+、-、、/这四种运算符,中缀表达式中只有一种括号:();
(2)输入的中缀表达式中只有整数,没有小数;
(3)假定输入是合法的。
很多文章或课本喜欢一步到位,直接讨论如何从中缀表达式计算结果。但对于初学者来说,跨度未免大了点。这里循序渐进,从易到难,先讨论如何将中缀表达式转化为后缀表达式,再讨论如何计算后缀表达式。最后在前面两步的基础上,讨论如何一步到位,直接计算中缀表达式的结果:
一、如何将中缀表达式转化为后缀表达式
在日常应用中,算术表达式中运算符总是出现在两个操作数之间,例如5(7-23)+8/2,这种形式称为中缀表达式。计算一个中缀表达式需要知道运算符的优先级和结合性。乘除是高优先级,加减是低优先级,优先级相同时他们都是左结合的,也就是从左计算到右。有括号就要计算括号内的表达式。
中缀表达式利于人的理解,但不便于计算机的处理。因此需要将中缀表达式转换成后缀表达式,以方便计算机处理。所谓后缀表达式就是将运算符放在运算数之后。后缀表达式也称为逆波兰表达式。
比如:
中缀表达式为:1+(2-3)4+4/2
对应后缀表达式为:1 2 3 - 4 + 4 2 / +
如何将一个中缀表达式转化为后缀表达式?我们需要借助栈的力量,用它来存放运算符。算法流程如下:
首先将各种运算符(包括括号)的优先级排列如下(数字越大,优先级越高):
1:(
2:+ -
3: /
4:)
对输入的中缀表达式从左到右遍历:
1)如果遇到数字,直接添加到后缀表达式末尾;
2)如果遇到运算符+、-、*、/:
先判断栈是否为空。若是,则直接将此运算符压入栈。若不是,则查看当前栈顶元素。若栈顶元素优先级大于或等于此操作符级别,则弹出栈顶元素,将栈顶元素添加到后缀表达式中,并继续进行上述判断。如果不满足上述判断或者栈为空,将这个运算符入栈。要注意的是,经过上述步骤,这个运算符最终一定会入栈。
3)如果遇到括号:
如果是左括号,直接入栈。如果是右括号,弹出栈中第一个左括号前所有的操作符,并将左括号弹出。(右括号别入栈)。
4)字符串遍历结束后,如果栈不为空,则弹出栈中所有元素,将它们添加到后缀表达式的末尾,直到栈为空。
二、计算后缀表达式
后缀表达式的计算就相当简单了。准备一个数字栈。从左到右扫描后缀表达式,如果是数字,放入数字栈。如果是符号,从数字栈中弹出两个数字,第一个取出的数字为右运算数,第二个为左运算数,进行运算。然后将结果放进数字栈中。如此反复,直到读完整个表达式后,留在数字栈中的那个数字就是最终结果。