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 rpnList=getListString(suffixExpression);
		System.out.println("rpnList= "+rpnList);
		int res=calcute(rpnList);
		System.out.println("计算的结果是= "+res);
		
	}
	//将逆波兰表达式依次将数据和运算符放入到ArrayList中
	public static List getListString(String suffixExpression){
		String[] split=suffixExpression.split(" ");
		List list=new ArrayList();
		for(String ele:split) {
			list.add(ele);
		}
		return list;
	}
	//完成对逆波兰表达式的运算,对List进行遍历
	public static int calcute(List ls) {
		//创建栈
		Stack stack = new Stack();
		//遍历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 toInfixExpressionList(String s){
		List ls=new ArrayList();
		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=48 && (c=s.charAt(i))<=57 ) {
					str+=c;//拼接
					i++;
				}
				ls.add(str);
			}
		}while(i
  • 定义运算符优先级高低的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 parseSuffixExpressionList(List ls){
		//定义两个栈
		Stack s1=new Stack();//符号栈
		//s2在算法中没有pop操作,且需要逆序输出,故可以不用s2栈而用ArrayList更简单操作
//		Stack s2=new Stack();//储存中间结果的栈
		List s2=new ArrayList();//存储中间结果的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 infixExpressionList=toInfixExpressionList(expression);
    System.out.println("中缀表达式转为为list= "+infixExpressionList);
    List 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 stack=new Stack<>();
	static List data=Collections.synchronizedList(new ArrayList());
	
	//方法:去除所有空白符
	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 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;icalcLevle(stack.peek())) && calcLevle(each)=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 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 list1=new ArrayList<>();
		for(int i=0;i

 

你可能感兴趣的:(Java数据结构)