1. 程式人生 > >BST性能分析&改進思路——平衡與等價

BST性能分析&改進思路——平衡與等價

成本 最終 角度 search none 提升 我們 因此 兩個

  • 極端退化

前面所提到的二叉搜索樹,已經為我們對數據集進行高效的靜態和動態操作打開了一扇新的大門。正如我們所看到的,BST從策略上可以看作是將之前的向量(動態數組)和鏈表結構的優勢結合起來,不過多少令我們有些失望的是:目前所實現的BST還有些稚嫩,表現在它的時間復雜度在極端情況仍未得到有效的控制。根據之前的內容,我們知道無論靜態or動態操作,它的時間上界都正比於樹的高度,即Oh),不過到目前為止,我們對於它的高度還沒有任何有效的控制方法。比如說,我們假設所有子節點個數都<=1,那麽整棵樹將會退化為一條單鏈,此時從邏輯結構上看,它就等價為一個鏈表。

技術分享圖片

那麽樹的高度h與節點數n則呈線性關系,也就是O(n)=n-1=h。這無論做漸進分析還是最壞分析,靜態操作和動態操作都需要高達O(n)的時間。

  • 平均高度

從客觀角度,我們還需要對BST的性能做系統分析。下面從兩種統計口徑分別給出針對BST平均高度的估算結果。

1.隨機生成

對於任意生成的一組關鍵碼詞條,長度為n,考慮所有可能的排列,比如n=3的情況。會得到這些形態的樹:

技術分享圖片

這樣創建的方式被稱為隨機生成,當詞條長為n時,一共就是n!種可能,平均高度為O(logN)。

2.隨機組成

也就是我們將所有n個關鍵碼視作n個互異的積木,在滿足BST規則的前提下,考察他們總共能夠拼出多少種拓撲結構互異的BST。可以證明這樣得出的總數為Catalan(n),是我們熟知的卡特蘭數,組合數學裏講過。這時候平均高度為O(N)。

那這兩種口徑得到的結果不一樣,到底哪種更為可信呢?對比一下

技術分享圖片

我們認為,後者更可信,原因在於前者是有所重復的,這種重復性就體現在不同的關鍵碼序列有可能會生成同一棵BST,比如對於n3的情況而言,這樣兩個輸入序列所生成的都是這樣同一棵BST

技術分享圖片

也就是說只要第一個插入的是居中的2,那麽分列於它左右的13究竟是按什麽次序輸入是沒有關系的。實際上不難理解,這個結論可以進一步地推廣,也就是說中位數,或者接近於中位數的關鍵碼越是被更早地插入,整體而言,這棵BST的高度也相應地會更低。這就意味著在前一種統計口徑中。

這類高度更低的BST,將會被以更高的重復度參與統計以及最終的平均估算這也是為什麽按照前一統計口徑所得到的估算值會相對更小。從這一觀點來看,前一統計口徑可以說是過於樂觀了,而後一統計口徑所得到的結論將更為可信。但對於在此非常在意樹高的我們來說,這並不是一個好消息,這意味著在天然的隨機意義下,這樣一個高度是超出我們承受範圍的,為了進一步地降低和控制這個高度,我們應該采取措施。

  • 理想+適度

為了控制樹的高度,我們或許首先應該弄明白:什麽樣的樹相對而言高度是更低的。根據之前的例子,在節點數目相對固定時,左右兄弟子樹的高度越是接近,全樹通常也會更加傾向於高度更低。也就是說——全樹越是接近於平衡,那麽它的高度也會傾向於更低。因此我們可以通過控制全樹的平衡度,以控制全樹的高度。關於樹的高度 我們有這樣一個結論,n個節點所組成的二叉樹,其高度最低不會少於log 2為底n的對數。

技術分享圖片

因此如果某棵樹的高度能夠達到這樣一個理想的下限,我們也稱之為理想平衡的。那麽哪些樹能夠達到這樣的理想平衡狀態呢?你應該會聯想起我們此前所講過的——完全二叉樹( Complete Binary Tree)。

技術分享圖片

是的,在這樣的樹中葉節點只能出現在最底層以及次底層。當然 其中最好最好不過的自然是所謂的滿二叉樹( Full Binary Tree)。

技術分享圖片

然而很遺憾——這樣的樹在實際應用中只能是可遇不可求的。

而且即便BST在某一個時刻能夠達到這樣高度緊湊的形式在接下來的動態操作過程中這樣一種完美的形式也是難以持續的因此所謂的理想平衡在實際應用中是不具任何意義的,理想平衡出現的可能性,非常非常的低。而且為了維護這樣的理想平衡,我們的計算成本也相應地會十分的高昂,會得不償失。

技術分享圖片

而真正可行的方法是,我們或許應該適度地放松平衡的標準,沒錯 適度地放松。你或許會聯想起我們此前的漸近分析,沒錯,那正是我們的一個法寶。實際上只要能夠保證全樹的高度,能夠從漸近的意義而言不超過logN,那麽也就可以稱之為是平衡的了。

技術分享圖片

因為這種平衡並非嚴格意義上的理想平衡所以我們也不妨稱之為適度平衡。這樣一種適當的放松,但又不失原則的方法,也就是我們通常所謂的退一步海闊天空。那麽相應地——能夠保持適度平衡的BST,也稱作平衡的二叉搜索樹(Balanced Binary Search Tree),簡稱BBST。也就是說,如果將所有的BST視作一個全集,那麽BBST只是其中的一個子集。對於目前而言其中任何一棵BBST,如果經過某次操作之後它不再保持適度平衡。也就是——會遊離到這個子集之外,果真如此的話我們就需要有一整套方法將這棵BST重新拉回到這個子集中,使它重新成為一棵BBST。像這樣:

技術分享圖片

那麽我們應該采用什麽樣的一些方法來做到這一點呢?或者先退一步,我們來考慮一下:我們可能采用哪些方法呢?

概括而言,所有這些方法都必須是所謂的等價變換。

  • 歧義=等價

所謂BST它的本質特征就是處處局部的順序性以及全局的單調性具體來說——只需要考察它的中序遍歷序列是否是單調的。然而只需考察樹的遍歷算法,我們就不難發現:結構不相同的兩棵BST的中序遍歷序列有可能是完全雷同的,這也就是我們所說的中序遍歷序列的歧義性。在某些場合中,比如中綴表達式的求值計算。這種中序遍歷序列的歧義性非常令人生厭,因為我們不得不通過一些辦法,來明確地辨析不同操作符之間的優先級關系。而針對BBST這樣的一個問題,歧義性卻變成了一個非常重要同時也是不可或缺的一種性質。

技術分享圖片

以這裏所給出的兩棵BST為例,不難看出它們都是由同一組關鍵碼所構成的,而且它們的中序遍歷序列是完全一樣的,然而反過來 我們也註意到,它們的拓撲結構也不盡相同。左側局部子樹的樹根是19,而在右側卻是16。當然,還可以很容易地舉出更多拓撲結構不盡相同,但中序遍歷序列卻相同的實例。任何一對這樣的BST也就稱作相互等價的BST。等價的BST之間在拓撲結構上雖然不盡相同,但也有其獨特的規律。

這種規律概括起來有兩句話:

技術分享圖片

第一:“上下可變”比如在這個例子中1916的祖先和後代關系就有可能在兩棵樹中彼此顛倒這也可以認為等價的BST在垂直方向有一定的自由度。然而我們的第二條規律則是“左右不能亂”這裏的左右自然是指中序遍歷序列確實相對於任何一個節點居於它右側的節點以及居於它左側的節點之間不能相互混淆。那麽在這樣一種上下存在一定的調整余地,但左右次序卻不得顛倒的規則下又該如何來實現BST之間的等價轉換呢?

  • 等價變換

實際上對於任何一棵BST之間的等價轉換都可以視作是由一系列的基本操作串接而成的而這種基本的變換無非兩類 彼此對稱其中一類如這個圖所示:

技術分享圖片

也就是說 如果節點V擁有一個左孩子C,而且它們屬下分別有三棵,在此命名為X Y Z的子樹,我們只需將這局部的兩個節點以及三棵子樹重新調整為這樣一種拓撲連接的形式。那麽無論是在此局部,還是在它們所屬的那棵全樹,順序性和單調性將依然保持。也就是說 全樹依然將是一棵BST。為了對此做一驗證,我們不妨來考察在此局部的中序遍歷次序——我們可以看到:無論是在變換之前 還是變換之後,在此局部的中序遍歷序列的次序必然是X C Y V 以及最後的Z。從效果來看 這樣一個變換,可以大致理解為是在此局部圍繞著節點V,做了一個順時針的旋轉。稱這種變換為zig。那麽這種旋轉的中心V——則是zig的參數

下面則是完全對稱的另一種基本操作:

技術分享圖片

因為它可以理解為是圍繞著節點V做了一次逆時針的旋轉操作,我們也形象地稱之為zag。在後續的章節中 我們將會看到包括AVL樹、 紅黑樹在內的各種BBST都分別精心地定義了某種適度平衡的準則,從而使得原本在其中的任何一棵BBST即便在經過某次操作之後,會暫時地遊離到這個邊界之外,我們也總是能夠通過一系列精巧地等價變換令它重新回到這個邊界以內,並重新成為一棵BBST

技術分享圖片

而在設計所有這些等價變換的組合時,我們始終不要忘了應該遵循兩個重要的準則:一個就是所謂的局部性。也就是說,我們執行的每一次等價變換都應該局限在某一常數規模的局部。比如對於我們剛剛介紹的zigzag操作而言,它們都局限在局部的VC兩個節點處。

技術分享圖片

如此一來它們所牽涉到的節點總數既然是常數,這類操作所需要的計算時間也可以嚴格地控制在常數的規模。

第二個需要嚴格遵守的是:在我們將一棵剛剛失衡的BBST重新恢復為一棵BST的過程中,累計需要執行的旋轉次數不要過多。比如至多不能超過logN次,這樣我們就可以有效地控制整個操作序列的長度、以及總體所需要的時間。後面我們會看到 任何一種BBST都必須至少滿足這樣一個logN的基本條件。但是在進一步的要求上,它們各自又有所差異——比如對於AVL樹而言,它的刪除操作只能剛剛達到這個及格線,而它的插入操作卻可以優化到常數。而我們在後面的後面將要介紹的紅黑樹,也就是Red Black Tree則可以進一步地將這兩種操作的性能同時提升到最優的常數。那麽接下來的一節,我們就首先來看看,AVL樹是如何達到這樣一個及格線的。

BST性能分析&改進思路——平衡與等價