Java-数据结构系列十:逆波兰计算器(含中缀表达式转后缀表达式)

1.逆波兰表达式(后缀表达式)的计算过程:

  • 例如:“(3+4)*5-6”对应的逆波兰表达式为“3 4 + 5 * 6 -”,针对后缀表达式使用栈求值的步骤:
  • 从左至右扫描,将3和4压入堆栈;
  • 遇到+运算符,弹出栈顶元素4和次顶元素3,计算3+4的值,得7后将7入栈;
  • 将5入栈;
  • 遇到*运算符,弹出7和5,计算乘积为35压入栈中;
  • 将6入栈;
  • 遇到-运算符,弹出栈顶元素和次顶元素,计算次顶元素-栈顶元素(35-6=29),即结果为29;

2.思路分析:

  • 输入是String格式的后缀表达式,需要将后缀表达式转换为对应的list,方便遍历表达式中的元素;
  • 对List进行遍历,配合栈,完成对逆波兰表达式的计算;

3.逆波兰计算器(只处理整数)代码实现:

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class PolandNotation {

	public static void main(String[] args) {
		//定义逆波兰表达式
		//(30+4)×5-26 => 30 4 + 5 × 26 -,使用空格隔开
		//4*5-8+60+8/2 => 4 5 * 8 - 60 + 8 + 2 /
		String suffixExpression="4 5 * 8 - 60 + 8 2 / +";
		//1.先将"3 4 + 5 × 6 -"放到ArrayList中;
		//2.将ArrayList传递给方法,遍历数组,配合栈完成计算
		List<String> rpnList=getListString(suffixExpression);
		System.out.println("rpnList= "+rpnList);
		int res=calcute(rpnList);
		System.out.println("计算的结果是= "+res);
		
	}
	//将逆波兰表达式依次将数据和运算符放入到ArrayList中
	public static List<String> getListString(String suffixExpression){
		String[] split=suffixExpression.split(" ");
		List<String> list=new ArrayList<String>();
		for(String ele:split) {
			list.add(ele);
		}
		return list;
	}
	//完成对逆波兰表达式的运算,对List进行遍历
	public static int calcute(List<String> ls) {
		//创建栈
		Stack<String> stack = new Stack<String>();
		//遍历ls
		for(String item:ls) {
			//使用正则表达式取出数
			if(item.matches("\\d+")) {//匹配多位数
				stack.push(item);
			}else {
				//若不是数字,则pop出两个数,再入栈
				int num2=Integer.parseInt(stack.pop());
				int num1=Integer.parseInt(stack.pop());
				int res=0;
				if(item.equals("+")) {
					res=num1+num2;
				}else if(item.equals("-")){
					res=num1-num2;//注意顺序
				}else if(item.equals("*")) {
					res=num1*num2;
				}else if(item.equals("/")) {
					res=num1/num2;
				}else {
					throw new RuntimeException("运算符有误");
				}
				//把结果res入栈
				stack.push(""+res);//将整型转为string
			}
		}
		//最后留在stack中的数据是运算结果
		return Integer.parseInt(stack.pop());
	}
}

4.中缀表达式转换为后缀表达式

  1. 初始化两个栈:运算符栈s1,存储中间结果的栈s2;
  2. 从左至右扫描中缀表达式;
  3. 遇到操作数时,将其压入s2;
  4. 遇到运算符时,比较其与s1栈顶运算符的优先级:
    • 如果s1为空,或栈顶运算符为左括号‘(’,则直接将运算符压入栈s1;
    • 否则,若优先级比栈顶运算符的高,也将运算符压入栈s1;
    • 否则,将s1栈顶的运算符弹出并压入到s2中,将遍历到的运算符继续与s1栈中新的栈顶运算符相比较;
  5. 遇到括号时:
    • 如果是左括号‘(’,则直接压入栈s1;
    • 如果是右括号‘)’,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃;
  6. 重复2到5,直到表达式的最右边;
  7. 将s1中剩余的运算符依次弹出并压入s2;
  8. 依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式;

5.中缀表达式转后缀表达式代码实现

  • 输入是String形式的表达式,将其首先转换为中缀表达式对应的List,方便遍历;
//方法:将中缀表达式转化为对应的list
	public static List<String> toInfixExpressionList(String s){
		List<String> ls=new ArrayList<String>();
		int i=0;//用于遍历中缀表达式字符串s的指针
		String str;//用于对多位数拼接
		char c;//每遍历到一个字符,放入到c
		do {
			//如果c是非数字,加入到ls中
			if((c=s.charAt(i))<48 || (c=s.charAt(i))>57){
				ls.add(""+c);
				i++;//i后移
			}else {//数字考虑多位数的问题
				str="";//将str置空
				while(i<s.length() && (c=s.charAt(i))>=48 && (c=s.charAt(i))<=57 ) {
					str+=c;//拼接
					i++;
				}
				ls.add(str);
			}
		}while(i<s.length());
		return ls;//返回数组
	}
  • 定义运算符优先级高低的Operation类,可以返回一个运算符对应的优先级数字
class Operation{
	private static int ADD=1;
	private static int SUB=1;
	private static int MUL=2;
	private static int DIV=2;
	//方法,返回对应的优先级数字
	public static int getValue(String operation) {
		int result=0;
		switch (operation) {
		case "+":
			result=ADD;
			break;
		case "-":
			result=SUB;
			break;
		case "*":
			result=MUL;
			break;
		case "/":
			result=DIV;
			break;
		case "(":
			break;
		case ")":
			break;
		default:
			throw new RuntimeException("运算符输入错误");
		}
		return result;
	}
}
  • 将得到的中缀表达式对应的List转换为后缀表达式对应的List
	public static List<String> parseSuffixExpressionList(List<String> ls){
		//定义两个栈
		Stack<String> s1=new Stack<String>();//符号栈
		//s2在算法中没有pop操作,且需要逆序输出,故可以不用s2栈而用ArrayList更简单操作
//		Stack<String> s2=new Stack<String>();//储存中间结果的栈
		List<String> s2=new ArrayList<String>();//存储中间结果的List
		//遍历ls
		for(String item:ls) {
			if(item.matches("\\d+")) {//如果是数,加入s2
				s2.add(item);
			}else if(item.equals("(")) {//左括号直接入栈s1
				s1.push(item);
			}else if(item.equals(")")) {
				//如果是右括号,依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将一对括号丢弃
				while(!s1.peek().equals("(")) {
					s2.add(s1.pop());
				}
				s1.pop();//重要,将"("丢弃,消除括号
			}else {
				//若item的优先级小于等于s1栈顶运算符的优先级,将s1栈顶的运算符弹出并加入到s2中,再将item与s1栈顶运算符比较
				//问题:缺少比较优先级高低的方法
				while(s1.size()!=0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)) {
					s2.add(s1.pop());
				}
				//最后将item压入s1栈
				s1.push(item);
			}
		}
		//将s1中剩余的运算符加入到s2中
		while(s1.size()!=0) {
			s2.add(s1.pop());
		}
		return s2;//s2是list,输出的顺序就是存入的顺序,即后缀表达式的正确顺序
	}
  • main方法测试:
public static void main(String[] args) {
    String expression="1+((2+3)*4+5)/5-5";
    List<String> infixExpressionList=toInfixExpressionList(expression);
    System.out.println("中缀表达式转为为list= "+infixExpressionList);
    List<String> suffixExpressionList=parseSuffixExpressionList(infixExpressionList)
    System.out.println("后缀表达式对应的list= "+suffixExpressionList);
    System.out.printf("expression: %s=%d",expression,calcute(suffixExpressionList));        

    System.out.println();
}

6.逆波兰计算器的完整版

package stack;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
import java.util.regex.Pattern;

public class ReversePolandMultiCalc {
	//匹配 + - * / ()运算符
	static final String SYMBOL="\\+|-|\\*|/|\\(|\\)";
	static final String LEFT="(";
	static final String RIGHT=")";
	static final String ADD="+";
	static final String MINUS="-";
	static final String TIMES="*";
	static final String DIVISION="/";
	
	static final int LEVEL_01=1;
	static final int LEVEL_02=2;
	static final int LEVEL_HIGH=Integer.MAX_VALUE;
	
	static Stack<String> stack=new Stack<>();
	static List<String> data=Collections.synchronizedList(new ArrayList<String>());
	
	//方法:去除所有空白符
	public static String replaceAllBlank(String s) {
		//\\s+匹配任何空白字符,包括空格、制表符、换页符等
		return s.replace("\\s+", "");
	}
	//方法:判断是不是数字 int double long float
	public static boolean isNumber(String s) {
		Pattern pattern=Pattern.compile("^[-\\+]?[.\\d]*$");
		return pattern.matcher(s).matches();
	}
	//方法:判断是否是运算符
	public static boolean isSymbol(String s) {
		return s.matches(SYMBOL);
	}
	//方法:匹配运算等级
	public static int calcLevle(String s) {
		if("+".equals(s)||"-".equals(s)) {
			return LEVEL_01;
		}else if("*".equals(s)||"/".equals(s)) {
			return LEVEL_02;
		}
		return LEVEL_HIGH;
	}
	//方法:匹配
	public static List<String> doMatch(String s) throws Exception{
		if(s==null||"".equals(s.trim())) {
			throw new RuntimeException("data is empty");
		}
		if(!isNumber(s.charAt(0)+"")) {
			throw new RuntimeException("data is illeagle,start not with a number");
		}
		s=replaceAllBlank(s);
		System.out.println("表达式字符串:"+s);
		String each;
		int start=0;
		for(int i=0;i<s.length();i++) {
			if(isSymbol(s.charAt(i)+"")) {
				each=s.charAt(i)+"";
				//栈为空
				if(stack.isEmpty() || LEFT.equals(each) ||((calcLevle(each)>calcLevle(stack.peek())) && calcLevle(each)<LEVEL_HIGH)){
					stack.push(each);
				}else if(!stack.isEmpty() && calcLevle(each)<=calcLevle(stack.peek())) {
					//栈非空,操作符优先级小于等于栈顶优先级时出栈入列,直到栈为空,最后操作符入栈
					while(!stack.isEmpty() && calcLevle(each)<=calcLevle(stack.peek())) {
						if(calcLevle(stack.peek())==LEVEL_HIGH) {
							break;
						}
						data.add(stack.pop());
					}
					stack.push(each);
				}else if(RIGHT.equals(each)) {
					//右括号,依次出栈直到空栈或遇到第一个左括号操作符,此时左括号出栈
					while(!stack.isEmpty() && LEVEL_HIGH>=calcLevle(stack.peek())) {
						if(LEVEL_HIGH==calcLevle(stack.peek())) {
							stack.pop();
							break;
						}
						data.add(stack.pop());
					}
				}
				start=i;//前一个运算符的位置
			}else if(i==s.length()-1 ||isSymbol(s.charAt(i+1)+"")) {
				each=start==0?s.substring(start,i+1):s.substring(start+1,i+1);
				if(isNumber(each)) {
					data.add(each);
					continue;
				}
				throw new RuntimeException("data not match number");
			}
		}
		//如果栈中还有元素,元素需要依次出栈入列
		Collections.reverse(stack);
		data.addAll(new ArrayList<>(stack));
		System.out.println(data);
		return data;
	}
	//方法:运算
	public static Double doTheMath(String s1,String s2,String symbol) {
		Double result;
		switch(symbol) {
			case ADD:
				result=Double.valueOf(s1)+Double.valueOf(s2);
				break;
			case MINUS:
				result=Double.valueOf(s1)-Double.valueOf(s2);
				break;
			case TIMES:
				result=Double.valueOf(s1)*Double.valueOf(s2);
				break;
			case DIVISION:
				result=Double.valueOf(s1)/Double.valueOf(s2);
				break;
			default:
				result=null;
		}
		return result;
	}
	
	//方法:计算结果
	public static Double doCalc(List<String> list) {
		Double d=0d;
		if(list==null||list.isEmpty()) {
			return null;
		}
		if(list.size()==1) {
			System.out.println(list);
			d=Double.valueOf(list.get(0));
			return d;
		}
		ArrayList<String> list1=new ArrayList<>();
		for(int i=0;i<list.size();i++) {
			list1.add(list.get(i));
			if(isSymbol(list.get(i))) {
				Double d1=doTheMath(list.get(i-2),list.get(i-1),list.get(i));
				list1.remove(i);
				list1.remove(i-1);
				list1.set(i-2, d1+"");
				list1.addAll(list.subList(i+1, list.size()));
				break;
			}
		}
		doCalc(list1);
		return d;
	}

	public static void main(String[] args) {
		String math="9+(3-1)*3+10/2";
		String math1="12.8+(2-3.55)*4+10/5.0";
		try {
			doCalc(doMatch(math1));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值