【程序人生】數據結構雜記(三)

說在前面

個人讀書筆記

棧(stack)是存放數據對象的一種特殊容器,其中的數據元素按線性的邏輯次序排列,故也可定義首、末元素。不過,儘管棧結構也支持對象的插入和刪除操作,但其操作的範圍僅限於棧的某一特定端。也就是說,若約定新的元素只能從某一端插入其中,則反過來也只能從這一端刪除已有的元素。禁止操作的另一端,稱作盲端

棧中元素接受操作的次序必然始終遵循所謂“後進先出”(last-in-first-out, LIFO)的規律:
從棧結構的整個生命期來看,更晚(早)出棧的元素,應爲更早(晚)入棧者;反之,更晚(早)入棧者應更早(晚)出棧。

棧中可操作的一端更多地稱作棧頂(stack top),而另一無法直接操作的盲端則更多地稱作棧底(stack bottom)。

棧的典型應用——逆序輸出

在棧所擅長解決的典型問題中,有一類具有以下共同特徵:

  • 首先,雖有明確的算法,但其解答卻以線性序列的形式給出;
  • 其次,無論是遞歸還是迭代實現,該序列都是依逆序計算輸出的;
  • 最後,輸入和輸出規模不確定,難以事先確定盛放輸出數據的容器大小。因其特有的“後進先出”特性及其在容量方面的自適應性,使用棧來解決此類問題可謂恰到好處。

示例:進制轉換

在這裏插入圖片描述
遞歸實現:
在這裏插入圖片描述迭代實現:
在這裏插入圖片描述

棧的典型應用—— 遞歸嵌套

示例:括號匹配

在這裏插入圖片描述
實際上,只要將push、pop操作分別與左、右括號相對應,則長度爲n的棧混洗,必然與由n對括號組成的合法表達式彼此對應。比如,棧混洗{ 3, 2, 4, 1 }對應於表達式"( ( ( ) ) ( ) )"。按照這一理解,藉助棧結構,只需掃描一趟表達式,即可在線性時間內,判定其中的括號是否匹配。

實例:
在這裏插入圖片描述
算法:
在這裏插入圖片描述

棧的典型應用——逆波蘭表達式

逆波蘭表達式(reverse Polish notation,,RPN)是數學表達式的一種,其語法規則可概括爲:
操作符緊鄰於對應的(最後一個)操作數之後。比如12+“1 2 +”即通常習慣的1+2“1 + 2”
按此規則,可遞歸地得到更復雜的表達式,比如RPN表達式
1 2 + 3 4 ^ *

即對應於常規的表達式
( 1 + 2 ) * 3 ^ 4

波蘭表達式求值算法:
在這裏插入圖片描述示例:
在這裏插入圖片描述
在這裏插入圖片描述

試探回溯法

八皇后問題

在這裏插入圖片描述

如圖,國際象棋中皇后的勢力範圍覆蓋其所在的水平線、垂直線以及兩條對角線。現考查如下問題:
nnn * n的棋盤上放置nn個皇后,如何使得她們彼此互不攻擊——此時稱她們構成一個可行的棋局。
對於任何整數n>=4n >= 4,這就是nn皇后問題。
由鴿巢原理可知,在nnnn列的棋盤上至多隻能放置nn個皇后。反之,nn個皇后在nnn * n棋盤上的可行棋局通常也存在。

皇后類:
在這裏插入圖片描述
既然每行能且僅能放置一個皇后,故不妨首先將各皇后分配至每一行。然後,從空棋盤開始,逐個嘗試着將她們放置到無衝突的某列。每放置好一個皇后,才繼續試探下一個。若當前皇后在任何列都會造成衝突,則後續皇后的試探都必將是徒勞的,故此時應該回溯到上一皇后。

示例:
在這裏插入圖片描述

迷宮尋徑問題

間區域限定爲由nnn * n個方格組成的迷宮,除了四周的圍牆,還有分佈其間的若干障礙物;只能水平或垂直移動。我們的任務是,在任意指定的起始格點與目標格點之間,找出一條通路(如果的確存在)。

格點是迷宮的基本組成單位,故首先需要實現Cell類
在這裏插入圖片描述
可見,除了記錄其位置座標外,格點還需記錄其所處的狀態。共有四種可能的狀態:
原始可用的(AVAILABLE)、在當前路徑上的(ROUTE)、所有方向均嘗試失敗後回溯過的(BACKTRACKED)、不可穿越的(WALL)。屬於當前路徑的格點,,需記錄其前驅和後繼格點的方向。
既然只有上、下、左、右四個連通方向,故以EAST、SOUTH、WEST和NORTH區分。
特別地,因尚未搜索到而仍處於初始 AVAILABLE狀態的格點,鄰格的方向都是未知的(UNKNOWN);經過回溯後處於BACKTRACKED狀態的格點,與鄰格之間的連通關係均已關閉,故標記爲NO_WAY。

在路徑試探過程中需反覆確定當前位置的相鄰格點
在這裏插入圖片描述
在這裏插入圖片描述
在確認某一相鄰格點可用之後,算法將朝對應的方向向前試探一步,同時路徑延長一個單元。
在這裏插入圖片描述
基於試探回溯策略實現尋徑算法:
在這裏插入圖片描述
從算法的中間過程及最終結果都可清晰地看出,這裏用以記錄通路的棧結構的確相當於忒修斯手中的線繩,它確保了算法可沿着正確地方向回溯。另外,這裏給所有回溯格點所做的狀態標記則等效於用粉筆做的記號,正是這些標記確保了格點不致被重複搜索,從而有效地避免了沿環路的死循環現象。

隊列

與棧一樣,隊列(queue)也是存放數據對象的一種容器,其中的數據對象也按線性的邏輯次序排列。隊列結構同樣支持對象的插入和刪除,但兩種操作的範圍分別被限制於隊列的兩端——若約定新對象只能從某一端插入其中,則只能從另一端刪除已有的元素。允許取出元素的一端稱作隊頭(front),而允許插入元素的另一端稱作隊尾(rear)。

由以上的約定和限制不難看出,與棧結構恰好相反,隊列中各對象的操作次序遵循所謂先進先出(first-in-first-out, FIFO)的規律:
更早(晚)出隊的元素應爲更早(晚)入隊者,反之,更早(晚)入隊者應更早(晚)出隊。

隊列應用——循環分配器

爲在客戶(client)羣體中共享的某一資源(比如多個應用程序共享同一CPU),一套公平且高效的分配規則必不可少,而隊列結構則非常適於定義和實現這樣的一套分配規則。

隊列應用——銀行服務模擬

結語

如果您有修改意見或問題,歡迎留言或者通過郵箱和我聯繫。
手打很辛苦,如果我的文章對您有幫助,轉載請註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章