1-3 Bags,Queues and Stacks
基本的抽象數據類型涉及若干對象的集合以及在這個集合上定義的一些操作,包括添加、刪除和集合中的對象等。下面主要以三種基本類型(Bag Stack Queue)來說明。
- 我們在用不同的方式表現集合中的元素時,通常也會產生不同的操作效率
- 使用泛型和循環可以顯著地簡化客戶端代碼
Bag
Bag 是一種收集元素並可以遍歷它們的數據結構,元素之間不分先後順序。Bag 不支持刪除元素的功能。
Bag 的 API
- 添加元素
- 判斷是否爲空
- 返回元素數量(集合大小)
Queue
Queue(隊列)是一種基於“先進先出” (FIFO)策略的元素集合,當進行遍歷時,元素的遍歷順序與他們被添加到集合時的順序一致
Queue 的 API
- 元素入隊(Enqueue)
- 元素出隊(Dequeue)
- 判斷是否爲空
- 返回元素數量(集合大小)
Stack
Stack(棧)是一種基於“後進先出” (LIFO)策略的元素集合,當進行遍歷時,元素的遍歷順序與他們被添加到集合時的順序剛好相反
Stack 的 API
- 元素入棧(Push)
- 元素出棧(Pop)
- 判斷是否爲空
- 返回元素數量(集合大小)
集合的具體實現
集合具體實現有兩種方式:數組 和 鏈表
集合實現的目標:
- 每種操作的時間複雜度獨立於集合的大小
- 佔用空間大小與集合大小必須是常數倍關係
數組
優點:
- 訪問指定元素的時間複雜度僅爲 O(1)
缺點:
- 長度不可變
- 長度與集合大小的關係模糊
動態調整數組長度彌補數組長度固定的缺點,保證不會發生溢出同時也不會浪費太多的空間,它的原理是:當數組沒有空間時,加倍數組的空間大小(即將這個數組複製到一個2倍長的數組中);當數組中元素個數減少至總空間的 1/4 時,減半數組的長度(即將這個數組複製到一個一半長的數組中)。這樣,空間利用率總在 1/4 到 1 之間,且不會發生溢出。然而,這樣的實現違背了集合實現目標的第一條。有時候,進行添加和刪除元素操作會使數組大小變化,這時就與集合大小有關。
鏈表
鏈表的遞歸定義是:
一個鏈表:
- 爲空(NULL)
或
- 是對一個節點的引用。這個節點包含一個元素和一個對某個鏈表的引用
算術表達式
算術表達式由算子(operand,即數字),運算符(operator)和括號組成。
完全括號化:每一處都使用括號來表明運算順序,而不是進行隱形的運算符優先級比較,例如
(1+((2+3)*(4*5)))
對於一個完全括號化的算術表達式,可以使用如下的遞歸方式去定義:
一個算術表達式是:
- 一個數字
或
- 一個左括號,後面跟着一個算術表達式,一個運算符和另一個算術表達式,然後以一個右括號結尾
中綴表達式: 我們經常使用的表達式寫法就是中綴表達式,即運算符在算子中間。運算符間有優先級差別,也有括號的存在。
後綴表達式:運算符在算子之後的表達式,不需要括號,運算符之間無優先級差別,比如 5 1 2 + 4 × + 3 −
。參見Reverse Polish notation
前綴表達式:
後綴表達式:運算符在算子之前的表達式,不需要括號,運算符之間無優先級差別,比如 − × ÷ 15 − 7 + 1 1 3 + 2 + 1 1
。參見Polish notation
計算算術表達式結果的算法
Dijkstra 算法 (Using two stacks)
從左到右一次取出一個元素,然後按照以下原則處理:
(完全括號化的情況)
- 如果是算子(數字),則將其壓入算子棧
- 如果是運算符,則將其壓入運算符棧
- 如果是左括號,則忽略
- 當遇到右括號時,從運算符棧彈出一個運算符,從算子棧彈出這個運算符需要數量的算子,進行運算,將結果壓入算子棧
(一般情況)
- 如果是算子(數字),則將其壓入算子棧
- 如果是運算符O1:
- 如果運算符棧空,則直接入棧
- 與運算符棧棧頂運算符O2進行優先級比較:
- 如果優先級小於等於O2,則彈出O2,從算子棧中彈出O2所需要數量的算子,進行計算,將結果壓入算子棧
- 如果優先級大於O2,則入棧
- 如果是左括號,則將其壓入運算符棧
- 當遇到右括號時:
- 如果運算符棧頂不是左括號,則從運算符棧中彈出一個運算符,再從算子棧中彈出運算符所需要數量的算子,進行計算,將結果壓入算子棧。循環這個過程,直到運算符棧頂是左括號爲止
- 如果運算符棧頂是左括號,則彈出左括號
- 當無元素可讀取時,如果運算符棧還有元素,則彈出運算符並彈出適量算子進行計算,將結果存入算子棧。循環這個過程直到運算符棧空,然後結束。否則直接結束。
其實,Dijkstra 算法的一般形式相當於將中綴表達式轉換成了後綴表達式,很多計算器就是基於這個算法制作的
完整的 Dijkstra 算法要複雜得多,包含了函數、右結合運算符、括號不匹配等多種情況,參見 Shunting Yard Algorithm