C++逆波兰表达式

今天在刷csp认证真题的时候,刷到了201903-2 二四十点。题目描述是判断一个表达式的结果是否等于24

例如有如下三个表达式,只有第三个是符合的,也就四则运算之后结果等于24

//1、5+4x5x5 = 105
//2、7-9-(9+8) = -19
//3、6x4+4/5 = 24

于是我就想到了把中缀表达式转换为逆波兰式,中缀表达式就是和我们平时书写习惯一样的表达式,运算符在操作数的中间,逆波兰式也叫后缀表达式,运算符在操作数之后,求结果非常的方便

因此顺便去复习了一下逆波兰式:

逆波兰式

假设有一个逆波兰式如下

abc*+

计算方式非常的简单,先从左到右遍历表达式,如果遇到数字,就直接将数字压入栈,如果遇到运算符,就弹出两个栈顶的数字,并且将运算结果压入栈顶。

c
b
a


例如在运算上述逆波兰式的时候,先将a,b,c入栈,现在遇到了‘*’ 于是弹出b,c,并且将b*c的结果压入栈,得到如下结果

b*c
a

现在下一个是‘+’,于是重复上面的操作,得到下面的结果,最后栈顶元素就是表达式的计算结果。这样一个求结果操作代码实现起来也比较简单。

b*c+a

当然问题的关键还是在于如何将中缀表达式转换为逆波兰式

中缀表达式转换为逆波兰式

首先定义输入的中缀表达式为in,输出序列为out,并且创建一个保存操作符的栈op

现在开始逐个遍历in的每一个字符,in[i]是操作数,那么直接将in[i]加入到out中,如果in[i]是运算符,那么有下面几种情况:

1、in[i]是'(',那么直接将in[i]压入op中

2、op为空,那么直接将in[i]压入op中

3、in[i]是')',那么开始依次弹出op的元素逐个加入到out中,直到遇到了'(',最后弹出'(',括号不需要加入out

4、in[i]是一般操作符: 此时比较op.top与in[i]的优先级关系:

        4.1、如果op.top优先级小于in[i]那么将in[i]压入op

        4.2、否则反复弹出op.top,直到op.top小于in[i]

实现细节:

在op创建的时候事先压入优先级非常小的符号,可以省去判断op是否为空的环节

另外,设in[i]为 op1,op.top为op2,当左括号作为op2的时候,优先级应该小于其他一般运算符,具体来说,可以为优先级赋值,例如+和-都赋值为2,*和/都赋值为3,左括号在作为op1的时候复制为4,作为op2的时候赋值为0

unordered_map<char, int> mp1{ {'0',-1},{'+',1},{'-',1},{'x',2},{'/',2},{'(',4} ,{')',0} };
unordered_map<char, int> mp2{ {'0',-1},{'+',1},{'-',1},{'x',2},{'/',2},{'(',0} ,{')',0} };
//左括号在入栈的时候拥有最高的优先级,但入栈以后应该当成优先级最低的运算符,否则会弹出左括号

完整代码:

#include<iostream>
#include<vector>
#include<unordered_map>
#include<stack>
#include<string>
using namespace std;
int main() {
	unordered_map<char, int> mp1{ {'0',-1},{'+',1},{'-',1},{'x',2},{'/',2},{'(',4} ,{')',0} };
	unordered_map<char, int> mp2{ {'0',-1},{'+',1},{'-',1},{'x',2},{'/',2},{'(',0} ,{')',0} };
	//左括号在入栈的时候拥有最高的优先级,但入栈以后应该当成优先级最低的运算符,否则会弹出左括号
	int n;
	cin >> n;
	while (n--) {
		string s;
		cin >> s;
		string out;
		stack<char> op;
		op.push(0);//在栈底放一个优先级小于任何运算符的标识符
		out.reserve(10);
		for (const char& c : s) {
			if (isdigit(c)) {
				out.push_back(c);
			}
			else if(c!=')') {//碰到右括号,则弹出左括号之前的所有运算符
				if (!op.empty()) {
					int c1 = mp1[c];
					int c2 = mp2[op.top()];
					while (c1 <= c2) {//如果当前运算符的优先级小于或者等于栈顶,那么弹出栈顶
						char o = op.top();
						op.pop();
						out.push_back(o);
						char t = op.top();
						c2 = mp2[op.top()];
					}
					op.push(c);
				}
				else {
					op.push(c);
				}
			}
			else {
				while (op.top() != '(') {
					char x = op.top();
					op.pop();
					out.push_back(x);
				}
				op.pop();
			}
		}
		while (op.size()>1) {
			char c = op.top();
			out.push_back(c);
			op.pop();
		}
		stack<int> res;
		cout << out << endl;
		for (const char& o : out) {
			if (isdigit(o)) {
				res.push(o - '0');
			}
			else {
				int x2 = res.top();
				res.pop();
				int x1 = res.top();
				res.pop();
				int ans = 0;
				switch (o) {
				case'x':
					ans = x1 * x2;
					break;
				case'/':
					ans = x1 / x2;
					break;
				case'+':
					ans = x1 + x2;
					break;
				case'-':
					ans = x1 - x2;
					break;
				}
				res.push(ans);
			}
		}
		cout << res.top() << endl;
	}
}

​

总结

逆波兰式是栈的一个经典应用,并且也用于计算机中表达式求值,实用性比较强,写这一篇主要意在帮自己复习早就忘得差不多的数据结构知识,

如果有什么错误我斗胆请各位大佬批评指正~

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值