標籤:

6/7 演算法題詳解:Evaluate RPN Expressions 如何求逆波蘭表達式(RPN)的值

本視頻來自於北美CS刷題神器BitTiger Pro。


首先我們先來看一下什麼是RPN表達式。RPN全稱逆波蘭表示法(Reverse Polish Notation)。RPN也被稱為後綴表示法,RPN表達式不同於我們日常見到的表達式,它的所有操作符置於操作數的後面,故而逆波蘭記法不需要括弧來標識操作符的優先。當滿足以下兩個條件,我們就可以稱一個表達式為RPN表達式:

  1. 它是由單個數字或者帶有負號的數字序列,比如-23,123
  2. 它是以「A B OP」形式展現的,其中A,B是RPN表達式,OP是運算符+、-、*和/。

比方說「三加四」用RPN的方式就寫作「3 ,4 +」。我們可以很明顯地看到我們熟悉的加號被放到了後面,這就是RPN表達式顯著的特徵。

事實上RPN表達式可以被構造成一棵二叉樹,比方說RPN表達式「3,4,+,2,*,1,+」,它的樹結構就如下圖所示:

圖中,3和4構成了最小最底層的樹結構,然後乘以2構成了上一層的數,最後加上1形成了完整的二叉樹。

為了讓你更加了解RPN表達式,下面給出一些例子,大家動手試著把他們轉化為我們平時在使用的表達式。

在計算機科學編程者中,RPN表達式非常流行。當給出的是一般的計算表達式,經常轉化為RPN表達式來執行計算。原因顯而易見,從編程角度看,RPN表示式是一種更加簡單的格式。

讓我們開始講解如何實現RPN表示式的計算。還是舉例說明,比如「3,4,+,2,*,1,+」。我們從左到右開始計算這個表達式,首先我們讀取3和4,然後得到一個「+」,計算3加4的結果,是7,作為臨時結果,然後繼續讀取,2和「*」,就將臨時結果7乘以2,得到臨時結果14,最後讀取1和「+」,將14加1,得到最終結果15。

在這幾步計算中我們發現:

  1. 我們需要存儲臨時結果;
  2. 當我們讀取到運算符,就需要申請最近獲得的兩個數字(包括臨時結果和讀取的數字),臨時結果的插入和刪除執行「後入先出(last-in,first-out)」的原則。

從上述發現我們可以用堆(stack)可以很好的滿足RPN表達式的計算場景。

我們給出的解決方案就是從左到右掃描字元串,使用堆的思想實現,當我們讀取到操作數(operand),push到堆頂,當我們讀取到操作符(operator),從堆頂pull出最上面的2個數字,進行計算,然後把結果在push到堆頂。

讓我們來看一個例子:

  • 讀取第一個字元,是1,它是一個數字,所以把它push到堆里。

  • 讀取下一個字元,是2,它是一個數字,所以把它push到堆里。

  • 讀取下一個字元,是3,它是一個數字,所以把它push到堆里。

  • 讀取下一個字元,是4,它是一個數字,所以把它push到堆里。

  • 讀取下一個字元,是「+」,它是一個運算符,按照我們的演算法,需要把堆頂的兩個元素拿出來進行計算,得到局部結果7。

  • 把局部結果7 push到堆頂。

  • 接下來到了第二個運算符,還是加號,需要把堆頂的兩個元素拿出來進行計算,得到局部結果9。

  • 把局部結果9 push到堆頂。

  • 接下來到了第三個運算符,還是加號,需要把堆頂的兩個元素拿出來進行計算,得到最終結果10。

實現代碼如下:

需要說明一下,在前文中我們提到需要使用堆來實現,但是在實際實現中,我們採用了Deque(雙端隊列,全名double-ended queue)來實現。事實上Deque是Java中一種普遍實現堆的方法。

回頭看一下代碼,首先中我們把傳入的表達式進行切割,然後是藉助分隔好的字元串數組進行循環,如果是數字,那就push到堆裡面,如果是運算符,按照運算符的類型分情況執行。等到數組訪問完,就返回最後一個堆裡面的元素。

最後我們分析一下性能。時間複雜度是O(n),比較好理解,我們就是遍歷整個字元串。空間複雜度是O(n),因為我們需要把每個原始數據和臨時結果存下來。


本視頻來自於北美CS刷題神器BitTiger Pro。

每月更新的BitTiger Pro是一個覆蓋了CS和數據科學方向最新高頻面試題的精講視頻庫。訂閱後,你可以在Code Evaluation System親自練習這道題。


推薦閱讀:

100 個數,如何遍歷得到所有全排列?
『一道很難的智力題』解法
我有一個1*n的格子帶,上面有n個單位格子,需要把其中m個格子染上色。我現在有三種演算法,哪種符合要求?
這套神奇的演算法,比網易雲音樂更懂你
遊戲中渲染層實體如何平滑的做插值?

TAG:算法 |