- package ch8;
- import java.util.LinkedList;
- import java.util.List;
- import java.util.Stack;
- /**
- * 四則混合運算表達式計算
- * @author Jinjichao
- *
- */
- public class Calculator {
- /**
- * 運算符枚舉
- * @author Jinjichao
- *
- */
- private enum Operator {
- ADD("+", 10), SUBTRACT("-", 10), MULTIPLY("*", 20), DIVIDE("/", 20),
- PARENTHESIS_LEFT("(", 100), PARENTHESIS_RIGHT(")", 100);
- private String operator;
- private int priority;
- private Operator(String operator, int priority) {
- this.operator = operator;
- this.priority = priority;
- }
- }
- /**
- * 操作數枚舉
- * @author Jinjichao
- *
- */
- private enum Operand {
- ONE("1"), TWO("2"), THREE("3"), FOUR("4"), FIVE("5"), SIX("6"),
- SEVEN("7"), EIGHT("8"), NINE("9"), ZERO("0"), POINT(".");
- private String operand;
- private Operand(String operand) {
- this.operand = operand;
- }
- }
- /**
- * 獲取字符串所對應的運算符枚舉
- * @param str
- * @return
- */
- private Operator getOperator(String str) {
- for (Operator op : Operator.values()) {
- if (str.equals(op.operator)) {
- return op;
- }
- }
- return null;
- }
- /**
- * 獲取字符串所對應的操作數枚舉
- * @param str
- * @return
- */
- private Operand getOperand(String str) {
- for (Operand op : Operand.values()) {
- if (str.equals(op.operand)) {
- return op;
- }
- }
- return null;
- }
- /**
- * 第1步: 將運算表達式字符串分解為運算表達式List
- *
- * @param exp
- * @return
- */
- private List<String> resolveExpr(String exp) {
- List<String> list = new LinkedList<String>();
- String temp = "";
- exp = exp.replace(" ", "");
- for (int i = 0; i < exp.length(); i++) {
- String str = exp.substring(i, i + 1);
- Operator op = getOperator(str);
- Operand od = getOperand(str);
- if (op != null) {
- if (!temp.isEmpty()) {
- list.add(temp);
- temp = "";
- }
- list.add(str);
- } else if (od != null) {
- temp += str;
- } else {
- System.out.println("表達式[" + str + "]非法! ");
- return null;
- }
- }
- if (!temp.isEmpty()) {
- list.add(temp);
- }
- //System.out.println(list);
- return list;
- }
- /**
- * 第2步: 將運算表達式List轉換為逆波蘭表達式List
- * @param expList
- * @return
- */
- private List<String> dealExpr(List<String> expList) {
- if(expList == null) {
- return null;
- }
- List<String> list = new LinkedList<String>();
- Stack<String> stack = new Stack<String>();
- for (String str : expList) {
- Operator op = getOperator(str.substring(0, 1));
- Operand od = getOperand(str.substring(0, 1));
- if (od != null) {
- //操作數直接入隊列
- list.add(str);
- } else if (op != null) {
- if (Operator.PARENTHESIS_LEFT.equals(op)) {
- //左括號入棧
- stack.push(str);
- } else if (Operator.PARENTHESIS_RIGHT.equals(op)) {
- //右括號: 循環將棧頂的運算符取出並存入隊列,直到取出左括號
- while (true) {
- if (stack.empty()) {
- System.out.println("缺少左括號! ");
- return null;
- } else if (Operator.PARENTHESIS_LEFT.operator.equals(stack.peek())) {
- stack.pop();
- break;
- } else {
- list.add(stack.pop());
- }
- }
- } else {
- //非括號類運算符
- if (!stack.empty()) {
- Operator top_op = getOperator(stack.peek());
- //當前運算符優先級大於棧頂運算符優先級,或者棧頂為左括號時,當前運算符直接入棧
- if(op.priority > top_op.priority
- || Operator.PARENTHESIS_LEFT.equals(top_op)) {
- stack.push(str);
- }
- //否則,將棧頂的運算符取出並存入隊列,然后將自己入棧
- else {
- list.add(stack.pop());
- stack.push(str);
- }
- } else {
- stack.push(str);
- }
- }
- }
- }
- while(!stack.empty()) {
- String str = stack.peek();
- if(Operator.PARENTHESIS_LEFT.operator.equals(str)) {
- System.out.println("缺少右括號! ");
- return null;
- } else {
- list.add(stack.pop());
- }
- }
- //System.out.println(list);
- return list;
- }
- /**
- * 操作數運算
- * @param x
- * @param y
- * @param op
- * @return
- */
- private String operation(String x, String y, Operator op) {
- double a = 0.0;
- double b = 0.0;
- try {
- a = Double.parseDouble(x);
- b = Double.parseDouble(y);
- } catch (NumberFormatException e) {
- System.out.println("操作數非法! ");
- e.printStackTrace();
- }
- switch (op) {
- case ADD:
- return String.valueOf(a + b);
- case SUBTRACT:
- return String.valueOf(a - b);
- case MULTIPLY:
- return String.valueOf(a * b);
- case DIVIDE:
- return String.valueOf(a / b);
- default:
- return null;
- }
- }
- /**
- * 第3步: 逆波蘭表達式運算
- * @param exp
- * @return
- */
- public String calculate(String exp) {
- List<String> expList = dealExpr(resolveExpr(exp));
- if(expList == null) {
- return null;
- }
- Stack<String> stack = new Stack<String>();
- for(String str : expList) {
- Operator op = getOperator(str.substring(0, 1));
- Operand od = getOperand(str.substring(0, 1));
- if(od != null) {
- stack.push(str);
- } else if (op != null) {
- //目前僅針對二元運算符
- String x = "";
- String y = "";
- if(!stack.empty()) {
- y = stack.pop();
- }
- if(!stack.empty()) {
- x = stack.pop();
- }
- if(!x.isEmpty() && !x.isEmpty()) {
- String result = operation(x, y, op);
- if(result == null) {
- return null;
- }
- stack.push(result);
- } else {
- return null;
- }
- }
- }
- return stack.pop();
- }
- }
測試一下:
- package ch8;
- /**
- * 測試
- * @author Jinjichao
- *
- */
- public class Test {
- public static void main(String[] args) {
- Calculator cal = new Calculator();
- String str = cal.calculate("( ( ( 15 / 3 ) + ( 1.5 * 2 ) + ( 20 - 12 )) - 3.2 +2.3 + 5 ) ");
- System.out.println("運算結果:" + str);
- }
- }
運行結果:
運算結果:20.1
其他的測試案例:
<IGNORE_JS_OP>
<IGNORE_JS_OP>
<IGNORE_JS_OP>
算法原理(對照截圖):
第1步、將表達式字符串分解為運算表達式List(將運算符和操作數分別分解出來)
第2步:將運算表達式List轉換為逆波蘭表達式List
第3步:逆波蘭表達式運算