在演算法書上看到了Dijkstra的表示式求值演算法,不斷地將括號包圍的子表示式替換為一個數值,最終就可以求得結果。相比於轉換成字尾表示式的演算法,該演算法很簡潔,但限制卻十分地大:必須將所有 expr op expr 用括號括起來,如:( 1 + ( ( 2 + 3 ) + ( 4 * 5 ) ) )。
Dijkstra演算法(PS:下面的實現中,每次讀到的字串s是一個一個的元素數字or運算子):
public class Evaluate { public static void main(String[] args) { Stack<String> ops = new Stack<String>(); Stack<Double> vals = new Stack<Double>(); while (!StdIn.isEmpty()) { // 讀取字元,如果是運算子則壓入棧 String s = StdIn.readString(); if (s.equals("(")) ; else if (s.equals("+")) ops.push(s); else if (s.equals("-")) ops.push(s); else if (s.equals("*")) ops.push(s); else if (s.equals("/")) ops.push(s); else if (s.equals("sqrt")) ops.push(s); else if (s.equals(")")) { // 如果字元為 ")",彈出運算子和運算元,計算結果並壓入棧 String op = ops.pop(); double v = vals.pop(); if (op.equals("+")) v = vals.pop() + v; else if (op.equals("-")) v = vals.pop() - v; else if (op.equals("*")) v = vals.pop() * v; else if (op.equals("/")) v = vals.pop() / v; else if (op.equals("sqrt")) v = Math.sqrt(v); vals.push(v); } // 如果字元既非運算子也不是括號,將它作為 double 值壓入棧 else vals.push(Double.parseDouble(s)); } StdOut.println(vals.pop()); } }
然後我就嘗試改進演算法,去除必須新增括號限制,思路也是將括號圍起來的子表示式求值然後替換,相對來看,改進後的演算法程式碼行數增加了25行左右(去除棧定義、數字識別的程式碼),但依然要比轉換成字尾表示式要簡單、容易很多
如:-1 + 2 * (3 * 3 - 10) => -1 + 2 * (-1) => -3
/////// stack begin ///////// type stack []interface{} func (s stack) empty() bool { return len(s) == 0 } func (s *stack) push(e interface{}) { *s = append(*s, e) } func (s *stack) pop() interface{} { lastIdx := len(*s) - 1 e := (*s)[lastIdx] *s = (*s)[:lastIdx] return e } /////// stack end ///////// func Calculate(expr string) int { numStack, opStack, curSubexprNumCount := stack{}, stack{}, stack{} expr = "(" + expr + ")" for i := 0; i < len(expr); i++ { ch := expr[i] switch ch { case '(': if !curSubexprNumCount.empty() { // 遇到下一個子表示式,則當前表示式數字個數加一 curSubexprNumCount.push(curSubexprNumCount.pop().(int) + 1) } curSubexprNumCount.push(0) opStack.push(ch) case '+', '-', '*', '/': opStack.push(ch) case ')': numStk, opStk := stack{}, stack{} // 正序化 numCount := curSubexprNumCount.pop().(int) for j := 0; j < numCount; j++ { numStk.push(numStack.pop()) } for op := opStack.pop().(byte); op != '('; op = opStack.pop().(byte) { opStk.push(op) } if len(numStk) == len(opStk) { // + 或 - 開頭的子表示式:-1+2... if op := opStk.pop().(byte); op == '-' { numStk.push(-numStk.pop().(int)) } } var tmp stack tmp.push(numStk.pop().(int)) for !opStk.empty() { x := numStk.pop().(int) switch opStk.pop().(byte) { case '+': tmp.push(x) case '-': tmp.push(-x) case '*': tmp.push(tmp.pop().(int) * x) case '/': tmp.push(tmp.pop().(int) / x) } } result := 0 for !tmp.empty() { result += tmp.pop().(int) } numStack.push(result) // 將求得的子表示式值放入棧中 default: if !unicode.IsDigit(rune(ch)) { continue } num := 0 for ; i < len(expr) && unicode.IsDigit(rune(expr[i])); i++ { num = 10 * num + int(expr[i]) - '0' } i-- // 退一步 numStack.push(num) curSubexprNumCount.push(curSubexprNumCount.pop().(int) + 1) } } result := 0 for !numStack.empty() { result += numStack.pop().(int) } return result }