C++基础算法之逆波兰

逆波兰算法

逆波兰式(Reverse Polish Notation,RPN,或逆波兰记法),也叫后缀表达式(将运算符写在操作数之后)。

那么什么是后缀表达式。

**中缀:**a+b

前缀(波兰):+ab

**后缀(逆波兰):**ab+

中缀 --> 前缀

  • 加括号:(a+b),从左到右,()代表一个表达式,遇到一个运算符,就将两边表达式打括号,一直到结束
  • 按括号提取符号:最开括号的中的运算符提至左括号前面,只是括号的前面
  • 最后得到逆波兰表达式子

( a +b ) * c + d - ( e + g ) * h

((((a + b) * c ) + d ) -( (e+g)) * h))

-+*+abcd*+egh

中缀 --> 后缀

  • 加括号:(a+b),从左到右,()代表一个表达式,遇到一个运算符,就将两边表达式打括号,一直到结束
  • 按括号提取符号:最开括号的中的运算符提至右括号前面
  • 最后得到逆波兰表达式子

( a +b ) * c + d - ( e + g ) * h

((((a + b) * c ) + d ) -( (e+g)) * h))

ab+c*d+eg+h*-

代码实现思路

这里提供的一个简单的实现思路

中缀转后缀

  1. 初始化两个栈:s1,s2

  2. 从左到右扫描表达式

  3. 遇到操作数,压入s1

  4. 遇到运算符

    • 如果s2为空,或者栈顶为 ( ,则直接入栈
    • 如果优先级比栈顶运算符高,也压入s2
    • 否则,将s2里面的运算符压入s1,在转到4
  5. 如果遇到 ) 则将s2中的运算符压入s1,知道遇到 ( 为止,将()丢弃

  6. 最后将剩余的s2全部压入s1

#include<iostream>
#include<sstream>
#include<stack>
#include<vector>
#include<map>

using namespace std;




int IsOper(char str, const vector<char> &oper) {
	for (int i = 0; i < oper.size(); i++) {
		if (oper[i] == str) return i;
	}
	return -1;
}

void Excharge(stack<char> &s2, stack<char> &s1) {
	while (!s2.empty()) {
		s1.push(s2.top());
		s2.pop();
	}
}

void Excharge_(stack<char>& s2, stack<char>& s1) {
	while (!s2.empty() && s2.top() != '(') {
		s1.push(s2.top());
		s2.pop();
	}
	if (s2.empty()) return;
	s2.pop();
}

void Show(stack<char> s1) {

	if (s1.empty()) {
		cout << endl;
		return;
	}
	char val = s1.top();
	s1.pop();
	Show(s1);
	cout << val;  
}

void ReversePolish(string expression) {
	stack<char> s1;
	stack<char> s2;
	vector<char> oper = { '*','/','+','-','(',')' };
	map<char, int> rank;
	rank['('] = 3;
	rank['*'] = 2;
	rank['/'] = 2;
	rank['+'] = 1;
	rank['-'] = 1;
	for (int i = 0; i < expression.size(); i++) {
		int ret = IsOper(expression[i], oper);
		if (ret < 0) {
			s1.push(expression[i]);   //压入栈内
		}
		else {
			if (s2.empty() || s2.top() == '(') {
				s2.push(expression[i]);
			}
			else if (expression[i] == ')') {
				Excharge_(s2, s1);
			}
			else if (rank[expression[i]] > rank[s2.top()]) {
				s2.push(expression[i]);
			}
			else {
				Excharge(s2, s1);
				s2.push(expression[i]);   // 栈为空,直接压入s2
			}
		}
	}
	Excharge(s2, s1);
	Show(s1);
}

int main(void) {

	ReversePolish("(1+2)*5");
	ReversePolish("1+2*5");
	ReversePolish("(a+b)*c+d-(e+g)*h");

	return 0;
}

这里我只是简单的采用了单字符作为变量,数字如果是两位数就会发生错误。,如果一开始就对堆栈中插入值进行一个分割处理,就可以实现超过两位数字的后缀计算了,但是这里我没有尝试,主要核心是针对逆波兰算法的原理。

输出结果为

12+5*
125*+
ab+c*d+eg+h*-

上述结果为生成的后缀表达式,这里在s1入栈时,可以进行同步的运算,运算规则如下

后缀计算
1)new出一个stack s1。
2)编译后缀表达式。
3)遇到数字压入s1。
4)遇到运算符,直接从pop出两个数字进行计算,然后在压入s1。
5)最后知道整个表达式遍历结束。

总结

中缀向后缀进行变化的核心是s2,我们可以认为s2是一个单调递减栈,但又不完全想,因为有括号()可以让某些运算符的“优先级”无限拔高!

这里我的理解的是()是一个整体的作用域,而左括号(可以看成是优先级最高的运算符,同样也是作用域的起始,而右括号)则是这个作用域收尾的时刻。

那么这样看的话,依然是只有高优先级的运算符在上,低优先级运算符在下,而作用域这个整体的优先级是最高的。

而使用单调递减栈的作用就是找到该元素右边第一个优先级小于自己的运算符,然后对前面的值进行运算的操作。


老规矩,有用二连,感谢大家了。。。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值