最近看了后缀表达式,之前也遇到过类似的面试题,遂写这篇博客,用于以后复用(其中表达式的校验并没有完成)。
import java.util.HashMap; import java.util.Map; import java.util.Stack; /** * 字符串表达式计算 * * 2018年2月26日上午11:18:08 */ public class StringExpressEvaluateUtil { /** * 符号优先级集合 * 该优先级别用于将中缀表达式转换成后缀表达式过程中使用 */ final static Map<String,Integer> SIGNP_RIORITY =new HashMap<>(); /** * 成对符号集合 * 暂时仅用于在将中缀表达式转换成后缀表达式过程中,校验到")",往前匹配左括号。 */ final static Map<String,Integer> SIGNP_PAIR =new HashMap<>(); private StringExpressEvaluateUtil(){ //不允许外部进行实例化 } private static StringExpressEvaluateUtil instance; private static StringExpressEvaluateUtil getInstance(){ if(instance == null) { return new StringExpressEvaluateUtil(); }else { return instance; } } static { SIGNP_RIORITY.put("+", 1); SIGNP_RIORITY.put("-", 1); SIGNP_RIORITY.put("*", 2); SIGNP_RIORITY.put("/", 2); SIGNP_RIORITY.put("(", -1); SIGNP_PAIR.put("(",-1); SIGNP_PAIR.put(")", 1); } /** * * @param srcExpress 原始表达式 * @return 后缀表达式,用于具体的计算使用 * @author riseSun * 2018年2月26日上午11:19:59 */ public String transformExpress(String srcExpress) { if(srcExpress == null) { throw new IllegalArgumentException("原始表达式不能为null"); } StringBuilder postfixExpress = new StringBuilder(); if("".equals(srcExpress.trim())) { return postfixExpress.toString(); } //校验原始表达式是否有误 checkSrcExpress(srcExpress); //符号栈 = 中缀->后缀中间产品 Stack<String> signStack = new Stack<String>(); /** * 这里为了简单,所以假定原始表达式中,各个字符间具有空格,这样有助于表达式中数据的获取,如下表达式示例中, * 要提取其中的数字30,以及10.2比较麻烦。 * 当然,如果没有空格,本人想到使用一下方法: * {假设表达式为:-9+(30-1)*3+10.2/2 } * 将其中的数字,用a、b、c、d、e...代替(记录代替的数字) ,这样可以使用toCharArray()或者split("");获取其中的字符。 * 获取到最终后缀表达式之后,将这些字母用原先的数字替换(这个替换过程可以在具体计算表达式的时候再做) */ for(String c: srcExpress.split(" ")) { switch (c) { case "+": case "-": case "*": case "/": operateSign(c,signStack,postfixExpress); break; case "(": //左括号直接压入到符号栈中 signStack.push(c); break; case ")": //右括号单独处理 operateParentheses(c,signStack,postfixExpress); break; default: //除符号外,其他的操作数直接追加到输出中 postfixExpress.append(" "+c); break; } } //最后将符号栈中剩余的符号,追加到输出中 while(!signStack.isEmpty()) { postfixExpress.append(" "+signStack.pop()); } return postfixExpress.toString(); } /** * 校验字符串表达式的正确性 * @param srcExpress * @author riseSun * 2018年2月26日下午2:55:26 */ public void checkSrcExpress(String srcExpress) { // if(false) { // throw new IllegalArgumentException("原始表达式存在错误"); // } } /** * 处理读取的符号 * @param newSign * @param signStack * @param postfixStack * @author riseSun * 2018年2月26日下午12:46:38 */ public void operateSign(String newSign,Stack<String> signStack,StringBuilder postfixExpress) { //满足空栈或者优先级大于符号栈顶元素的优先级时,将符号直接压入栈中 if(signStack.isEmpty() || (SIGNP_RIORITY.get(newSign) > SIGNP_RIORITY.get(signStack.peek()))) { signStack.push(newSign); }else {//出栈,直到栈顶元素优先级<=新符号,或者空栈 //将符号打印到后缀栈中 postfixExpress.append(" "+signStack.pop()); operateSign(newSign, signStack, postfixExpress); } } /** * 处理右括号 * 从符号栈中压出元素,并将元素压入到结果栈中, * 直到符号栈中压出相反的符号为止 * @param newSign * @param signStack * @param postfixStack * @author riseSun * 2018年2月26日下午12:47:16 */ public void operateParentheses(String newSign,Stack<String> signStack,StringBuilder postfixExpress) { while(!signStack.isEmpty() && !isPair(newSign,signStack.peek())) { postfixExpress.append(" "+signStack.pop()); } //废弃栈中的左括号 signStack.pop(); } /** * 判断两个符号是否是对称符号,例如(),[],{}..等 * 由SIGNP_PAIR来维护对称关系 * @param sign * @param anotherSign * @author riseSun * 2018年2月26日下午12:53:30 */ public boolean isPair(String sign,String anotherSign) { Integer signInt; Integer anotherSignInt; return (signInt = SIGNP_PAIR.get(sign)) !=null && (anotherSignInt = SIGNP_PAIR.get(anotherSign)) !=null && signInt.intValue()+anotherSignInt.intValue() == 0; } /** * 根据后缀表达式进行计算 * @param postfixStack * @return * @author riseSun * 2018年2月26日下午2:20:05 */ public static double evaluate(String postfixStack) { //用于存放操作数以及中间计算结果 Stack<String> evaluteStack = new Stack<>(); String[] source = postfixStack.split(" "); for(String s : source) { double result; switch(s) { case "+": result = Double.parseDouble(evaluteStack.pop()) + Double.parseDouble(evaluteStack.pop()); evaluteStack.push(result+""); break; case "-": double meiosis = Double.parseDouble(evaluteStack.pop());//减数 double minuend = Double.parseDouble(evaluteStack.pop());//被减数 result = minuend - meiosis; evaluteStack.push(result+""); break; case "*": result = Double.parseDouble(evaluteStack.pop()) * Double.parseDouble(evaluteStack.pop()); evaluteStack.push(result+""); break; case "/": double divisor = Double.parseDouble(evaluteStack.pop());//除数 double dividend = Double.parseDouble(evaluteStack.pop());//被除数 result = dividend / divisor; evaluteStack.push(result+""); break; default: //操作数直接压入栈中 evaluteStack.push(s); break; } } //返回最终留在栈中的结果 return Double.parseDouble(evaluteStack.pop()); } /** * 根据中缀表达式,直接计算结果 * @param midExpress * @return * @author riseSun * 2018年2月26日下午3:02:28 */ public static double evaluateMidExpress(String midExpress) { StringExpressEvaluateUtil instance = getInstance(); return StringExpressEvaluateUtil.evaluate(instance.transformExpress(midExpress).trim()); } }