【算法】E.W.Dijkstra算術表達式求值

算術表達式求值算法

咱們要學習的一個棧的用例同時也是展現泛型的應用的一個經典例子,就是用來計算算術表達式的值,例如數組

( 1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) )函數

若是將4乘以5,把3加上2,取它們的積而後加上1,就獲得了101。但Java系統是如何完成這些運算的呢?不須要研究Java系統的構造細節,咱們也能夠編寫一個Java程序來解決這個問題。它接受一個輸入字符串(表達式)並輸出表達式的值。爲了簡化問題,首先來看一下這份明確的遞歸定義:算術表達式多是一個數,或者是由一個左括號、一個算術表達式、一個運算符、另外一個算術表達式和一個右括號組成的表達式。簡單起見,這裏定義的是未省略括號的算術表達式,它明確地說明了全部運算符的操做數——你可能更熟悉形如 1 + 2 * 3 的表達式,省略了括號,而採用優先級規則。咱們將要學習的簡單機制也能處理優先級規則,但在這裏咱們不想把問題複雜化。爲了突出重點,咱們支持最多見的二元運算符*、+、-和/,以及只接受一個參數的平方根運算符sqrt。咱們也能夠輕易支持更多數量和種類的運算符來計算多種你們熟悉的數學表達式,包括三角函數、指數和對數函數。咱們的重點在於如何解釋由括號、運算符和數字組成的字符串,並按照正確的順序完成各類初級算術運算操做。如何纔可以獲得一個(由字符串表示的)算術表達式的值呢?E.W.Dijkstra在20世紀60年代發明了一個很是簡單的算法,用兩個棧(一個用於保存運算符,一個用於保存操做數)完成了這個任務,其實現過程以下,運行軌跡如輸出結果。學習

表達式由括號、運算符和操做數(數字)組成。咱們根據如下4種狀況從左到右逐個將這些實體送入棧處理:lua

  • 將操做數壓入操做數棧
  • 將運算符壓入運算符棧
  • 忽略左括號
  • 在遇到有括號時,彈出一個運算符,彈出所需數量的操做數,並將運算符和操做數的運算結果壓入操做數棧

在處理完最後一個右括號以後,操做數棧上只會有一個值,它就是表達式的值。這種方法乍一看有些難以理解,但要證實它可以計算獲得正確的值很簡單:每當算法遇到一個被括號包圍並由一個運算符和兩個操做數組成的子表達式時,它都將運算符和操做數的計算結果壓入操做數棧。這樣的結果就像在輸入中用這個值代替了該子表達式,所以用這個值代替子表達式獲得的結果和原表達式相同。咱們能夠反覆應用這個規律並獲得一個最終值。例如,用該算法計算如下表達式獲得的結果都是相同的spa

( 1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) )code

( 1 + ( ( 5 ) * ( 4 * 5 ) ) )blog

( 1 + ( 5 * 20 ) )遞歸

( 1 + 100 )字符串

101

程序:

 1 package com.beyond.algs4.experiment;
 2 
 3 import com.beyond.algs4.lib.Stack;
 4 import com.beyond.algs4.std.StdIn;
 5 import com.beyond.algs4.std.StdOut;
 6 
 7 public class Evaluate {
 8 
 9     public static void main(String[] args) {
10         Stack<String> ops = new Stack<String>();
11         Stack<Double> vals = new Stack<Double>();
12         // 讀取字符,若是是運算符則壓入棧
13         while (!StdIn.isEmpty()) {
14             String s = StdIn.readString();
15             if         (s.equals("("))                ;
16             else if (s.equals("+"))        ops.push(s);
17             else if (s.equals("-"))        ops.push(s);
18             else if (s.equals("*"))        ops.push(s);
19             else if (s.equals("/"))        ops.push(s);
20             else if (s.equals("sqrt"))    ops.push(s);
21             else if (s.equals(")")) {
22                 // 若是符號爲「)」, 彈出運算符和操做數,計算結果並壓入棧
23                 String op = ops.pop();
24                 double v = vals.pop();
25                 if        (op.equals("+"))    v = vals.pop() + v;
26                 else if (op.equals("-"))    v = vals.pop() - v;
27                 else if (op.equals("*"))    v = vals.pop() * v;
28                 else if (op.equals("/"))    v = vals.pop() / v;
29                 else if (op.equals("sqrt"))    v = Math.sqrt(v);
30                 vals.push(v);
31             }
32             else vals.push(Double.parseDouble(s));            
33             StdOut.println("操做數棧:" + vals.toString());
34             StdOut.println("運算符棧:" + ops.toString());
35         }
36         StdOut.println(vals.pop());
37     }
38 
39 }

輸出:

( 1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) )
操做數棧:
運算符棧:

操做數棧:1.0 
運算符棧:

操做數棧:1.0 
運算符棧:+ 

操做數棧:1.0 
運算符棧:+ 

操做數棧:1.0 
運算符棧:+ 

操做數棧:2.0 1.0 
運算符棧:+ 

操做數棧:2.0 1.0 
運算符棧:+ + 

操做數棧:3.0 2.0 1.0 
運算符棧:+ + 

操做數棧:5.0 1.0 
運算符棧:+ 

操做數棧:5.0 1.0 
運算符棧:* + 

操做數棧:5.0 1.0 
運算符棧:* + 

操做數棧:4.0 5.0 1.0 
運算符棧:* + 

操做數棧:4.0 5.0 1.0 
運算符棧:* * + 

操做數棧:5.0 4.0 5.0 1.0 
運算符棧:* * + 

操做數棧:20.0 5.0 1.0 
運算符棧:* + 

操做數棧:100.0 1.0 
運算符棧:+ 

操做數棧:101.0 
運算符棧:

 

 

參考資料:

算法 第四版  謝路雲 譯 Algorithms Fourth Edition [美] Robert Sedgewick, Kevin Wayne著

http://algs4.cs.princeton.edu/home/

源碼下載連接:

http://pan.baidu.com/s/1c0Ao7Bi

相關文章
相關標籤/搜索