package study.java.hanyx.rpn; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Stack; /** * 逆波兰表达式计算的实现 * @author 韩元旭 */ public class ReversePolishNotation { private static final Map<String, Integer> SIGN_PRIORITY = new HashMap<String, Integer>(); private static final String SIGN_LEFT = "("; private static final String SIGN_RIGHT = ")"; private static final String SIGN_PLUS = "+"; private static final String SIGN_MINUS = "-"; private static final String SIGN_TIMES = "*"; private static final String SIGN_DIVIDE = "/"; static { // 设置符号优先级 SIGN_PRIORITY.put(SIGN_LEFT, 0); SIGN_PRIORITY.put(SIGN_RIGHT, 0); SIGN_PRIORITY.put(SIGN_PLUS, 1); SIGN_PRIORITY.put(SIGN_MINUS, 1); SIGN_PRIORITY.put(SIGN_TIMES, 2); SIGN_PRIORITY.put(SIGN_DIVIDE, 2); } /** * 计算表达式的值 * @param exp * @return */ public static String calculate(String exp) { Stack<Double> stack = new Stack<Double>(); List<String> rpnList = create(exp); int length = rpnList.size(); for (int i = 0; i < length; i++) { String temp = rpnList.get(i); if (isNumber(temp)) { stack.push(Double.parseDouble(temp)); } else { double number2 = stack.pop(); double number1 = stack.pop(); double rst = 0; if (SIGN_PLUS.equals(temp)) { rst = number1 + number2; } else if (SIGN_MINUS.equals(temp)) { rst = number1 - number2; } else if (SIGN_TIMES.equals(temp)) { rst = number1 * number2; } else if (SIGN_DIVIDE.equals(temp)) { rst = number1 / number2; } stack.push(rst); } } //返回结果 double result = stack.pop(); if ((int)result == result) { return String.valueOf((int)result); } else { return String.valueOf(result); } } /** * 根据表达式字符串创建逆波兰表达式List * @param exp * @return */ private static List<String> create(String exp) { // 拆分表达式 List<String> expList = parse(exp); // 存放逆波兰表达式 List<String> rpnList = new LinkedList<String>(); // 存放运算符 Stack<String> stack = new Stack<String>(); for (String c: expList) { // 如果是数字,直接放到逆波兰链表的最后 if (isNumber(c)) { rpnList.add(c); } else { // 如果不是数字 // 如果是左括号,则直接将左括号压入栈 if (SIGN_LEFT.equals(c)) { stack.push(c); } else if (SIGN_RIGHT.equals(c)) { // 如果是右括号 // 进行出栈操作,直到栈为空或者遇到第一个左括号 while (!stack.isEmpty()) { // 将栈顶字符串做出栈操作 String tempC = stack.pop(); if (!SIGN_LEFT.equals(tempC)) { // 如果不是左括号,则将字符串直接放到逆波兰链表的最后 rpnList.add(tempC); } else { // 如果是左括号,退出循环操作 break; } } } else { // 如果栈内为空 if (stack.isEmpty()) { // 将当前字符串直接压栈 stack.push(c); } else { // 如果栈不为空 // 比较栈顶字符串与当前字符串的优先级, // 如果栈顶元素的优先级大于等于当前字符串,则进行出栈操作 // 将栈顶元素直接放到逆波兰链表的最后 // 直到栈内为空,或者当前元素的优先级不小于栈顶元素优先级 while (!stack.isEmpty() && !isPrior(stack.peek(), c)) { rpnList.add(stack.pop()); } // 如果当前符号大于栈顶元素优先级. // 将当前字符串直接压栈 stack.push(c); } } } } // 如果栈不为空,则将栈中所有元素出栈放到逆波兰链表的最后 while (!stack.isEmpty()) { rpnList.add(stack.pop()); } return rpnList; } /** * 拆分表达式为List * @param exp * @return */ private static List<String> parse(String exp) { // 四则运算解析 123 + 456 List<String> expList = new ArrayList<String>(); int length = exp.length(); // 123+456 StringBuffer sb = new StringBuffer();//123 for (int i = 0; i < length; i++) { String t = exp.substring(i, i + 1); if (isNumber(t)) { //如果是数字和之前的数字拼到一起 sb.append(t); } else { //如果是符号,先要把之前的数字加入并清空数字,再加入符号 if (sb.length()>0) { //前提是数字存在.. expList.add(sb.toString()); sb.delete(0, sb.length()); } expList.add(t); } } //如果存在,加入结尾的数字 if (sb.length()>0) { expList.add(sb.toString()); } return expList; } /** * 优先级是否高于栈顶元素 * @param pop * @param c * @return */ private static boolean isPrior(String pop, String c) { return SIGN_PRIORITY.get(c) > SIGN_PRIORITY.get(pop); } /** * 判断是否是数字 * @param sign * @return */ private static boolean isNumber(String sign) { //如果不是符号即为数字 return SIGN_PRIORITY.get(sign) == null; } /** * 测试 * @param args */ public static void main(String[] args) { String exp = "1000+100.0*99-(600-3*15)/(((68-9)-3)*2-100)+10000+7*71"; int times = 100000; long time1 = System.currentTimeMillis(); for (int i = 0; i < times; i++) { calculate(exp); } long time2 = System.currentTimeMillis(); System.out.println(times+"次耗时: " + (time2-time1) + " ms"); System.out.println("The Answer is : " + calculate(exp)); System.out.println("The Answer is : " + (1000+100.0*99-(600-3*15D)/(((68-9)-3)*2-100)+10000+7*71)); } }