codeup 1918 简单计算器
题目描述:
读入一个只包含 + ,-,×, / 的非负整数计算表达式,计算该表达式的值。
输入格式:
测试输入包含若干测试用例,每个测试用例占一行,每行不超过200个字符,整数和运草符之间用一个空格分隔。没有非法表达式。当一行中只有0时输入结束,相应的结果不要输出。
输出格式:
对每个测试用例输出1行,即该表达式的值,精确到小数点后2位。
样例输入:
30 / 90 - 26 + 97 - 5 - 6 - 13 / 88 * 6 + 51 / 29 + 79 * 87 + 57 * 92
0
样例输出:
12178.21
思路
题目给出的是中缀表达式,所以要计算它的值主要是两个步骤:(如果不懂什么是前缀表达式和后缀表达式请看:https://www.cnblogs.com/chensongxian/p/7059802.html和https://www.cnblogs.com/zzliu/p/10801113.html)
①中缀表达式转后缀表达式。
②计算后缀表达式。
下面分别讲一下这两步。
步骤1:中缀表达式转后缀表达式
①设立一个操作符栈,用以临时存放操作符;设立一个数组或者队列,用以存放后缀表
达式。
②从左至右扫描中缀表达式,如果碰到操作数(注意:操作数可能不止一位,因此需要
一位一位读入然后合并在一起,具体实现见代码),就把操作数加入后缀表达式中。
③如果碰到操作符op,就将其优先级与操作符栈的栈顶操作符的优先级比较。
(1)若op的优先级高于栈顶操作符的优先级,则压入操作符栈。
(2)若op的优先级低于或等于栈顶操作符的优先级,则将操作符栈的操作符不断弹出到
后缀表达式中,直到op的优先级高于栈顶操作符的优先级。
④重复上述操作,直到中缀表达式扫描完毕,之后若操作符栈中仍有元素,则将它们依
次弹出至后缀表达式中。
(1)所谓操作符的优先级即它们计算的优先级,其中乘法 == 除法 > 加法==减法,在具体实
现上可以用map建立操作符和优先级的映射,优先级可以用数字表示,例如乘法和除法优先
级为1,加法和减法优先级为0。
(2)关于为什么当op高于栈顶时就压入操作符栈,这里举一个例子;
对中缀表达式3 + 2×5,显然如果先计算加法3 + 2会引起错误,必须先计算乘法2×5。当
从左到右扫描时,加号先进入操作符栈,而由于乘号优先级大于加号,其必须先计算,因此
在后缀表达式中乘号必须在加号前面,于是在栈中乘号要比加号更靠近栈顶,以让其先于加
号进入后缀表达式。
(3)关于为什么当op等于栈顶时不能直接压入操作符栈,这里举一个例子:
对中缀表达式2 / 3×4,如果设定优先级相等时直接压入操作符栈,那么算法步骤如下:
a)2进入后缀表达式,当前后缀表达式为2。
b) / 进入操作符栈,当前操作符栈为 /*。
c)3进入后缀表达式,当前后缀表达式为23。
d) * 与操作符栈的栈顶元素 / 比较,相等,压入操作符栈,当前操作符栈为/。
e)4进入后缀表达式,当前后缀表达式为234。
f)中缀表达式扫描完毕,操作符栈非空,将其全部弹入后缀表达式,最终后缀表达式变
为234*/。
g)计算该后缀表达式,发现其实变成了2 / (3×4),显然这跟原来中缀表达式的计算结果
完全不同。
(4)本题没有出现括号,但是如果出现括号,处理方法也很简单,遇到括号时:
,如果是左括号‘(’,就压入操作符栈;如果是右括号‘)’,就把操作符栈里的元
素不断弹出到后缀表达式直到碰到左括号‘(’ ,此时将这一对括号丢弃。
步骤2:计算后缀表达式
从左到右扫描后缀表达式,如果是操作数,就压入栈;如果是操作符,就连续弹出两个操作数(注意:后弹出的是第一操作数,先弹出的是第二操作数),然后进行操作符的操作,
生成的新操作数压入栈中。反复直到后缀表达式扫描完毕,这时栈中只会存在一个数,就是最终的答案。
(1)注意除法可能导致浮点数,因此操作数类型要设成浮点型。
(2)题目中说肯定是合法表达式,因此上面操作一定能够成功。但如果题目表明可能出现
非法表达式,那就要注意每一步使用的对象是否合法。
#include<iostream>
#include<string>
#include<stack>
#include<fstream>
#include<queue>
#include<map>
using namespace std; struct node {
double num;
char op;
bool flag;
}; string str; //中缀表达式
stack<node> s; //操作符栈
queue<node> q; //后缀表达式序列
map<char, int> oop; //操作符和优先级的映射 void change() { //将中缀表达式转换为后缀表达式
node temp;
for (int i = 0; i < str.length();) {
if (str[i] >= '0' && str[i] <= '9') {
temp.flag = true;
temp.num = str[i++] - '0';//记录这个操作数的第一个数位
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() && oop[str[i]] <= oop[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;
while (!q.empty()) {//只要后缀表达式非空
cur = q.front();//cur记录首元素
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() {
oop['+'] = oop['-'] = 1;//设定操作符的优先级
oop['*'] = oop['/'] = 2;
// freopen("d://in.txt","r",stdin);
while (getline(cin, str), str != "0") {
for (string::iterator it = str.begin(); it != str.end(); it++) {
if (*it == ' ') str.erase(it);//把表达式中的空格全部去掉。
}
while (!s.empty()) s.pop();//将栈初始化
change();//将中缀表达式转化为后缀表达式
printf("%.2f\n", cal());//计算并 输出后缀表达式的值
}
return 0;
}
//若中缀表达式中存在着括号:
void change() { //将中缀表达式转换为后缀表达式
node temp;
for (int i = 0; i < str.length();) {
if (str[i] == '(') {
temp.op = '(';
s.push(temp);
i++;
}
else if (str[i] == ')') {
while (s.top().op != '(') {
q.push(s.top());
s.pop();
}
s.pop();
i++;
}
else if (str[i] >= '0' && str[i] <= '9') {
temp.flag = true;
temp.num = str[i++] - '0';//记录这个操作数的第一个数位
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() && oop[str[i]] <= oop[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();
}
}
前缀表达式是从右至左扫描中缀表达式的,如果中缀表达式要转换为前缀表达式不太方便。
若将中缀表达式转换为前缀表达式:
步骤一:
- 初始化两个栈:运算符栈s1,储存中间结果的栈s2(上面转换为后缀表达式时采用的是队列的方式储存后缀表达式,如果也采用栈的形式的话,输出结果的逆序才是后缀表达式)
- 从右至左扫描中缀表达式
- 遇到操作数时,将其压入s2
- 遇到运算符时,比较其与s1栈顶运算符的优先级
- 如果s1为空,或栈顶运算符为右括号“)”,则直接将此运算符入栈
- 否则,若优先级比栈顶运算符的较高或相等,也将运算符压入s1
- 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4-1)与s1中新的栈顶运算符相比较
- 遇到括号时
- 如果是右括号“)”,则直接压入s1
- 如果是左括号“(”,则依次弹出S1栈顶的运算符,并压入S2,直到遇到右括号为止,此时将这一对括号丢弃
- 重复步骤2至5,直到表达式的最左边
- 将s1中剩余的运算符依次弹出并压入s2。
- 依次弹出s2中的元素并输出,结果即为中缀表达式对应的前缀表达式。
前缀表达式的计算机求值:
从右至左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 和 次顶元素,栈顶元素在运算符前这一点与后缀运算符不同),并将结果入栈;重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果。
codeup 1918 简单计算器的更多相关文章
- 1.C#WinForm基础制作简单计算器
利用c#语言编写简单计算器: 核心知识点: MessageBox.Show(Convert.ToString(comboBox1.SelectedIndex));//下拉序号 MessageBox.S ...
- 菜鸟学Android编程——简单计算器《一》
菜鸟瞎搞,高手莫进 本人菜鸟一枚,最近在学Android编程,网上看了一些视频教程,于是想着平时手机上的计算器应该很简单,自己何不尝试着做一个呢? 于是就冒冒失失的开撸了. 简单计算器嘛,功能当然很少 ...
- PAT 06-1 简单计算器
想看一般简单计算器实现的看客不好意思了,这不是你想要点东西,此处题设为“只能进行加减乘除”.“都是整数”.”优先级相同“和"从左到右".此题来自PAT(http://www.pat ...
- php大力力 [005节] php大力力简单计算器001
2015-08-22 php大力力005. php大力力简单计算器001: 上网看视频,看了半天,敲击代码,如下: <html> <head> <title>简单计 ...
- PHP实现简单计算器
<!--简单的计算器--> <!DOCTYPE html> <html> <head> <title>PHP实现简单计算器</titl ...
- c#部分---网吧充值系统;简易的闹钟;出租车计费;简单计算器;对战游戏;等额本金法计算贷款还款利息等;随机生成10个不重复的50以内的整数;推箱子;
网吧充值系统namespace ConsoleApplication1 { class Program { struct huiyuan { public string name; public st ...
- JavaWeb学习记录(二十)——Model1模式(javaBean+jsp)实现简单计算器案例
¨JSP技术提供了三个关于JavaBean组件的动作元素,即JSP标签,它们分别为: ¨<jsp:useBean>标签:用于在JSP页面中查找或实例化一个JavaBean组件. ¨< ...
- 一个用WPF做的简单计算器源代码
一.界面设计XAML代码 <Window x:Class="fengjisuanqi.MainWindow" xmlns="http://schemas.micro ...
- hdu 1237 简单计算器
题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=1237 简单计算器 Description 读入一个只包含 +, -, *, / 的非负整数计算表达式, ...
随机推荐
- 1月22日第二轮空投来袭,SPC算力福利币究竟能带来什么?
行情数据显示,比特币于14日23时30分再次突破40000美元,市值回升至7400亿美元.根据行情频道数据,比特币于14日2时展露上行态势,价格于34000美元附近起跳,至12时站上37000美元.此 ...
- JDK环境解析,安装和目的
目录 1. JDK环境解析 1.1 JVM 1.2 JRE 1.3 JDK 2. JDK安装 2.1 为什么使用JDK8 2.1.1 更新 2.1.2 稳定 2.1.3 需求 2.2 安装JDK 2. ...
- Github Action 快速上手指南
前言 各位读者,新年快乐,我是过了年匆忙赶回上海努力搬砖的蛮三刀. Github之前更新了一个Action功能(应该是很久以前了),可以实现很多自动化操作.用来替代用户自己设置的自动化脚本(比如:钩子 ...
- 看完了进程同步与互斥机制,我终于彻底理解了 PV 操作
尽人事,听天命.博主东南大学硕士在读,热爱健身和篮球,乐于分享技术相关的所见所得,关注公众号 @ 飞天小牛肉,第一时间获取文章更新,成长的路上我们一起进步 本文已收录于 CS-Wiki(Gitee 官 ...
- 基于Docker Compose部署分布式MinIO集群
一.概述 Minio 是一个基于Go语言的对象存储服务.它实现了大部分亚马逊S3云存储服务接口,可以看做是是S3的开源版本,非常适合于存储大容量非结构化的数据,例如图片.视频.日志文件.备份数据和容器 ...
- PVE更新WEB管理地址
PVE也是一台Linux系统,如果PVE更换了网络环境,比如从家里拿到了办公室,那么就需要对其更新网络,才能让其它机器访问到它的8006管理地址. 具体做法是通过修改配置文件来更改IP. 更新网卡配置 ...
- Nginx常用内核参数优化,安装,基本命令
1.内核参数配置,默认的Linux内核参数考虑的是通用的场景,明显不符合用于支持高并发访问web服务的定义,所以需要修改Linux内核参数,使得Nginx可以拥有更高的性能.可以通过修改 /etc/s ...
- 有钱人买钻石+dfs中使用贪心
有钱人买钻石 ECNU-3306 题解:这个题目,乍一看以为是dp背包,可是数据量却那么大,只有1,5,10,25四种面额的硬币,每种数量若干,要使得能够刚好兑换成功总金额,在此前提下,还要使得硬币数 ...
- VMware 虚拟机安装(使用CentOS镜像)
(1)启动虚拟机,点击"文件"-->"新建虚拟机",选择安装类型,推荐"典型". (2)选择稍后安装操作系统 ...
- 关于Java中Collections.sort和Arrays.sort的稳定性问题
一 问题的提出 关于Java中Collections.sort和Arrays.sort的使用,需要注意的是,在本文中,比较的只有Collections.sort(List<T> ele ...