codeup 1918简单计数器_计算中缀和后缀表达式

点击查看代码
/*
给出一个只包含+,-,x,/的非负整数计算表达式,计算该表达式的值,结果保留2位小数点
表达式不超过200个字符,整数和运算符之间用一个空格分隔,没有非法表达式。当一行中只有0时输入结束,相应的结果不要输出
输入样例1:
30 / 90 - 26 + 97 - 5 - 6 - 13 / 88 * 6 + 51 / 29 + 79 * 87 + 57 * 92
0
输出样例1:
12178.21

输入样例2:
1 + 3 * 5 / 4 * 8 / 9 * 6 * 2 / 3 / 7 + 3 * 8 / 2
0
输出样例2:
14.90
*/

/*
解题思路:
1、题目给出的是中缀表达式,计算过程分两个步骤:
	(1)把中缀表达式转为后缀表达式
	(2)计算后缀表达式
2、把中缀表达式转为后缀表达式步骤:
	(1)设立一个操作符栈s,临时存放操作符。设立一个队列q,存放后缀表达式
	(2)从左至右扫描中缀表达式,如果遇到操作数,则将操作数放入后缀表达式中(注意操作数可能不止一位,因此需要逐位读取,最后再合并为一个操作数)
	(3)如果遇到操作符op,则将它与操作符栈的栈顶操作符进行优先级比较
		1)如果op的优先级高于栈顶操作符的优先级,则将op压入操作符栈中
		2)如果op的优先级低于等于栈顶操作符的优先级,则将操作符栈的栈顶操作符不断弹出到后缀表达式中,直到op的优先级高于栈顶操作符
	(4)重复上述步骤,直到中缀表达式扫描完成。如果扫描结束后操作符栈中还有元素,则将它们依次弹出到后缀表达式中
	(5)操作符优先级使用map<string,int>容器存储,"*"和"/"的优先级映射为2,"+"和"-"的优先级映射为1
	疑问1:为什么操作符op优先级高于栈顶操作符时要压入操作符栈中?
	举例中缀表达式3+2*5,如果先计算加法3+2会出错,必须先计算乘法2*5。从左往右扫描中缀表达式时,加号先进入操作符栈,而乘号优先级高于加号,
	为了在后缀表达式中先计算乘法,乘号必须在加号前面,所以在操作符栈中乘号要比加号更接近栈顶,所以优先级高于栈顶操作符的op要压入操作符栈中
	疑问2:为什么操作符op优先级等于栈顶操作符时不压入操作符栈中?
	距离中缀表达式2/3*4,如果设置操作符op优先级等于栈顶操作符时压入栈中,则算法步骤如下:
		1)2进入后缀表达式q,q:2
		2)/进入操作符栈s,s:/
		3)3进入后缀表达式,q:2 3
		4)*与操作符栈的栈顶操作符优先级比较,优先级相同,压入操作符栈,s:/ *
		5)4进入后缀表达式,q:2 3 4
		6)中缀表达式扫描完成,操作符栈非空,将栈中元素逐个弹出到后缀表达式,q:2 3 4 * /
		7)计算后缀表达式:2/(3*4),与原来的中缀表达式不同,结果错误,所以优先级相同的操作符op不能压入操作符栈中
注意:本题输入的表达式都没有括号,如果表达式中出现括号,则要在步骤2的(3)中先判断,如果op是左括号就压入操作符栈中。
	  如果op是右括号,则把操作符栈中的元素全部弹出到后缀表达式,直到遇到左括号时停止
3、计算后缀表达式
	(1)从左到右扫描后缀表达式,如果是操作数,则压入栈s中,如果是操作符,则连续弹出两个操作数(注意:先弹出的是第2个操作数temp2,
		后弹出的是第1个操作数temp1,顺序关系是temp1/temp2,而不是temp2/temp1),然后根据操作符的符号进行相应计算(如"+"则进行加法),
		计算结果存入栈s中。重复以上操作,直到后缀表达式扫描完成,此时栈中只会存在一个数,它就是最终的计算结果
注意:1、除法可能出现浮点数,所以操作数类型要设置为浮点型
	  2、使用string型变量存储输入的表达式str,然后扫描str,如果当前位是空格,则用str.erase(str[i])去掉空格,将不含空格的str转换为后缀表达式
*/

#include<iostream>
#include<cstdio>
#include<string>
#include<stack>
#include<queue>
#include<map>
using namespace std;

//存储表达式str[i]的每一位
struct node { 
	double num; //操作数
	char op; //操作符
	bool flag; //true表示str[i]是操作数,false表示str[i]是操作符
};

string str;	//存储输入的表达式
stack<node> s; //先用于存储操作符的栈,后用于存储计算结果
queue<node> q; //存储后缀表达式的队列
map<char, int> op; //操作符优先级映射,加法/减法映射为1,乘法/除法映射为2

//将中缀表达式转换为后缀表达式
void Change() {	
	node temp; //存储str[i]
	for (int i = 0; i < str.size();) { //扫描表达式str
		if (str[i] >= '0' && str[i] <= '9') { //如果str[i]是数字
			temp.flag = true; //temp标记为数字
			temp.num = str[i] - '0'; //减去ASCII码'0'转换为数字
			i++; //检查下一位,判断是否还是数字(操作数可能有多位,如100有3位)
			while (i < str.size() && str[i] >= '0' && str[i] <= '9') { //下一位还是数字,则它属于该操作数的低位
				temp.num = temp.num * 10 + (str[i] - '0'); //更新这个数字=高位*10+低位
				i++; //继续检查下一位,直到超出str边界或遇到操作符
			}
			q.push(temp); //将这个操作数存入后缀表达式的队列中
		}
		else {  //如果str[i]是操作符
			temp.flag = false; //temp标记为操作符
			//只要操作符栈的栈顶元素比该操作符优先级高
			//就把操作符栈的栈顶元素弹出到后缀表达式的队列中
			while (!s.empty() && op[str[i]] <= op[s.top().op]) {
				q.push(s.top());
				s.pop(); //栈顶元素弹出后要手动删除
			}
			temp.op = str[i];
			s.push(temp); //把str[i]操作符压入操作符栈中
			i++; //检查下一位
		}
	}
	//扫描完表达式str后,如果操作符栈中还有操作符,就把它们逐个弹出到后缀表达式队列中
	while (!s.empty()) {
		q.push(s.top());
		s.pop(); //栈顶元素弹出后要手动删除
	}
}

//计算后缀表达式
double Cal() { 
	node cur, temp;	//cur存储后缀表达式队首元素,temp存储每轮的计算结果
	double temp1, temp2; //存储第1和第2个操作数,注意顺序,如temp1/temp2不能写为temp2/temp1
	while (!q.empty()) { //只要后缀表达式队列非空
		cur = q.front(); //cur记录队首元素
		q.pop(); //手动删除队首元素
		if (cur.flag == true) s.push(cur); //如果是操作数,则压入栈中
		else { //如果是操作符,则进行计算
			temp2 = s.top().num; //先弹出temp2
			s.pop(); //元素弹出后要手动删除
			temp1 = s.top().num; //再弹出temp1
			s.pop(); //元素弹出后要手动删除
			temp.flag = true; //temp存储计算结果,flag设为true表示temp是操作数
			if (cur.op == '+') temp.num = temp1 + temp2; //cur是加号,则进行加法
			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循环有两个判断条件,先读入str,然后执行循环内的代码,然后再读入str,如果str等于数字0,则结束循环,否则继续执行
	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()); //计算后缀表达式并输出计算结果 */
	}
	/* 书上写的会报错
	while (getline(cin, str), str != "0") {
		for (string::iterator it = str.end(); it != str.begin(); it--) {
			if (*it == ' ') str.erase(it); //去掉输入表达式中的所有空格
		}
		while (!s.empty()) s.pop(); //初始化栈
		Change(); //将中缀表达式转换为后缀表达式
		printf("%.2f\n", Cal()); //计算后缀表达式
	}	*/
	return 0;
}

posted @ 2022-09-29 21:17  zhaoo_o  阅读(12)  评论(0编辑  收藏  举报