Java数据结构(逆波兰表达式)

Java数据结构(逆波兰表达式)
最近在学习java数据结构的相关知识,记录一下学习的内容,算是每天的总结,今天学习的是栈相关的内容,然后学习前缀,中缀,后缀表达式,下面介绍一下,这三种表达式的含义

  1. 中缀:中缀表达式,是我们最常见的表达式,就是日常算式,比如4+9-90*(5-1),这种结构是人类能够了解并且计算的表达式但是计算机却不好理解
  2. 后缀:后缀表达式,这里先不说后缀表达式是什么,先说怎么用,对于中缀表达式1+((2+3)*4)-5,它对应的后缀表达式是:[1, 2, 90, +, 8, *, +, 230, -],在这里我们从左向右扫描后缀表达式,对于数字就入栈,如果遇到符号,就弹出两个元素进行计算,并将结果入栈,最后栈中的元素就是我们想要的运算结果,不相信可以演算:1,2,90,遇到+,进行运算:1,92,8,遇到乘号,进行运算:1,736,遇到加号开始运算:737,230,遇到减号,开始运算:507,也就是说,利用后缀表达式和栈,我们就可以利用计算机迭代轻易的求出表达式的解
  3. 前缀表达式,也成为波兰表达式,对于波兰表达式,它同样具备和后缀表达式相同的功能,那就是表达式的求值,只不过他需要我们从右向左遍历手上的前缀表达式
    下面是前缀表达式的求值代码
public static int sovle(LinkedList<String> opt) {
		Stack<String> stack = new Stack<String>();
		for(String str:opt) {
			if(str.matches("\\d+")) {
				//如果是数字
				stack.add(str);
			}
			else {
				//如果是操作符
				String o1 = stack.pop();
				String o2 = stack.pop();
				Integer result = func(o1, o2, str);
				stack.push(result.toString());
			}
		}
		String num = stack.pop();
		return Integer.parseInt(num);
	}

但是我们的问题往往是,我们如何才能拿到后缀表达式呢,或者说,如何才能将日常的中缀表达式转化成后缀表达式呢,这就涉及到了今天学习的内容,为了便于理解,我们举一个不太恰当的例子,我们把后缀表达式的构建过程看成是梁山好汉的排座次的过程
在这里插入图片描述
我们将表达式中的符号进行简单的类比,数字就相当于平民英豪,运算符相当于当过官的英豪,而对于括号可能有点不同,因为括号不属于运算符,我们暂且归为官的一种,但是他们是有家室的官员,因为左右括号总是成对出现(这该死的爱情),下面介绍排位规则,一众英豪来到忠义堂前,按照市井次序排成一列,也就是我们最常见的中缀表达式,按照从左到右的次序一个个上前验明正身,对于平民英豪,因为其贫苦出身,直接进入忠义堂,依次落座
在这里插入图片描述
那么,1就做到第一把交椅,遇到第二个英豪,也就是加号,它属于当过官的英豪,未免有些规矩,他的规矩就是,他们做过官的要坐在一块,而且还要做领导层
在这里插入图片描述
所以就单独做了一块地,而且,根据做官的大小,他们也有尊卑之分,这体现到排位中就是,如果我的优先级小于等于我前面人的优先级,说明我的官衔大(一品大于二品,hhh原谅我在瞎编),我就会说:快滚出来,给小爷让个位置,这时前面的操作符出栈,然后无家可归(可怜),于是就去到隔壁,压入隔壁的栈中,但是此时我前面又会有一个人(如果栈没有空的话),此时我还会重复我的操作,但是对于左括号妹妹,我也会温柔一些,会让她坐我前面,但是对于,右括号哥哥,他就比较暴躁(比普通运算符暴躁,对于左括号都是直接进栈,她不会检测前面坐的是谁,而对于右括号,他只允许他前面是左括号,于是就有了下面的样子
在这里插入图片描述
下一个入栈的是右括号,他会讲:我只要我的括号妹妹,你们都给我消失,所以前面的运算符们一个个出栈,去到隔壁,直到遇到左括号,左括号也出栈和右括号一块,远走高飞,于是我们看到了如下结果
在这里插入图片描述
最后市井英豪尽数入栈,但是分居不可取,乃将官府人员的栈顶元素,一次弹出,压入隔壁,组成的就是后缀表达式的前身

下面是从韩顺平老师那里copy的官方说法:众所周知,excel从来都只是一个画图工具
中缀转后缀
* 案例:1+((2+3)*4)-5
* 1. 对于拿到手的中缀表达式,我们要先建立两个栈s1,s2
* 2.从左向右扫描表达式,遇到数字就将其入栈s2
* 3.如果遇到的是操作符,就先比较其于s1栈顶操作符的优先级
* 3.1当然,有可能s1此时为空栈,我们就可以直接将操作符入栈
* 3.2或者如果运算符是(也直接入栈即可
* 3.3如果如果运算优先级高于 栈顶元素,则将运算符入栈
* 3.4否则,将s1中操作符弹出,压入到s2中,然后用新的栈顶元素
* 与之比较,重复步骤三
* 4. 如果是括号,对于左括号,直接入栈
* 如果遇到的是右括号,就直接将s1中的元素安抚弹出并压入s2,直到遇到左括号
* 最后将两个括号舍弃
* 5.将s1中剩余元素,弹出,压入s2
下面是代码实现:

package com.shunping.poland;

import java.util.LinkedList;
import java.util.Stack;

public class Poland {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//拿到中缀表达式737-230
		String str = "1 + ( ( 2 + 90 ) * 8 ) - 230";
		//表达式打成list便于调用
		LinkedList<String> pol = strTolist(str);
		//进行逆波兰转换
		//System.out.println(pol.toString());
		LinkedList<String> opt = toPoland(pol);
		System.out.println("后缀:"+opt.toString());
		System.out.println("运算结果:"+sovle(opt));
	}
	
	public static LinkedList<String> strTolist(String str) {
		LinkedList<String> result = new LinkedList<String>();
		String[] strs = str.split(" ");
		for(String item:strs) {
			result.add(item);
		}
		return result;
	}
	
	public static LinkedList<String> toPoland(LinkedList<String> pol) {
		Stack<String> opt = new Stack<String>();
		LinkedList<String> num = new LinkedList<String>();
		for(String str:pol) {
			//如果扫描到数字就直接入栈num
			if(str.matches("\\d+")) {
				num.add(str);
			}
			else {
				//如果是运算符或者括号
				if(opt.isEmpty()||str.equals("(")) {
					//如果运算符栈为空,或者得到的是一个左括号,直接入栈
					opt.push(str);
				}
				else if(str.equals(")")) {
					//如果遇到右括号,就将opt中元素弹出并放入num,知道遇到左括号
					while(!opt.peek().equals("(")) {
						num.add(opt.pop());
					}
					//弹出左括号
					opt.pop();
				}
				else {
					//而且此时应该可以想到,栈内不存在一个配对的右括号
					//就算有,也应该在前面匹配过了,最多有一个左括号
					//此时str为运算符,而且此时,栈一定不为空
					while(true) {
						if(opt.isEmpty()) {
							opt.push(str);
							break;
						}
						else if(opt.peek().equals("(")||priority(str)>priority(opt.peek())) {
							//如果此时栈顶为左括号入栈
							//如果此时操作符大于栈顶元素,则入栈
							opt.push(str);
							break;
						}
						else {
							//此时运算符等级小于等于栈顶元素
							//opt栈顶元素压入num
							num.add(opt.pop());
							//这里没有跳出循环
							//也就是说,当前操作符会接着和下一个栈顶元素比较
						}
					}
				}
			}
		}
		while(!opt.isEmpty()) {
			//将操作符压入num
			num.add(opt.pop());
		}
		return num;
	}
	
	public static int priority(String opt) {
		if(opt.equals("+")||opt.equals("-")) {
			return 1;
		}
		else {
			return 2;
		}
	}
	
	public static int sovle(LinkedList<String> opt) {
		Stack<String> stack = new Stack<String>();
		for(String str:opt) {
			if(str.matches("\\d+")) {
				//如果是数字
				stack.add(str);
			}
			else {
				//如果是操作符
				String o1 = stack.pop();
				String o2 = stack.pop();
				Integer result = func(o1, o2, str);
				stack.push(result.toString());
			}
		}
		String num = stack.pop();
		return Integer.parseInt(num);
	}
	
	public static Integer func(String x, String y, String opt) {
		Integer xx = Integer.parseInt(x);
		Integer yy = Integer.parseInt(y);
		if(opt.equalsIgnoreCase("+")) {
			return xx+yy;
		}
		else if(opt.equalsIgnoreCase("-")) {
			return yy-xx;
		}
		else if(opt.equalsIgnoreCase("*")) {
			return xx*yy;
		}
		else if(opt.equalsIgnoreCase("/")) {
			return yy/xx;
		}
		else {
			return 0;
		}
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值