平衡二叉樹 AVL
在上一個專題中,我們在談論二叉查詢樹的效率的時候。不同結構的二叉查詢樹,查詢效率有很大的不同(單支樹結構的查詢效率退化成了順序查詢)。如何解決這個問題呢?關鍵在於如何最大限度的減小樹的深度。正是基於這個想法,平衡二叉樹出現了。
平衡二叉樹的定義 (AVL—— 發明者為Adel'son-Vel'skii 和 Landis)
平衡二叉查詢樹,又稱 AVL樹。 它除了具備二叉查詢樹的基本特徵之外,還具有一個非常重要的特點:它 的左子樹和右子樹都是平衡二叉樹,且左子樹和右子樹的深度之差的絕對值(平衡因子 ) 不超過1。 也就是說AVL樹每個節點的平衡因子只可能是-1、0和1(左子樹高度減去右子樹高度)。
那麼如何是二叉查詢樹在新增資料的同時保持平衡呢?基本思想就是:當在二叉排序樹中插入一個節點時,首先檢查是否因插入而破壞了平衡,若 破壞,則找出其中的最小不平衡二叉樹,在保持二叉排序樹特性的情況下,調整最小不平衡子樹中節點之間的關係,以達 到新的平衡。所謂最小不平衡子樹 指離插入節點最近且以平衡因子的絕對值大於1的節點作為根的子樹。
平衡二叉樹的操作
1. 查詢操作
平衡二叉樹的查詢基本與二叉查詢樹相同。
2. 插入操作
在平衡二叉樹中插入結點與二叉查詢樹最大的不同在於要隨時保證插入後整棵二叉樹是平衡的。那麼調整不平衡樹的基本方法就是: 旋轉 。
1) 繞某元素左旋轉
80 90
/ \ 左旋 / \
60 90 ---- -> 80 120
/ \ / \ /
85 120 60 85 100
/
100
a) BST樹 b ) AVL樹
分析一下:在插入資料100之前,a圖的B ST樹只有80節點的平衡因子是-1(左高-右高),但整棵樹還是平衡的。加入100之後,80節點的平衡因子就成為了-2,此時平衡被破壞。需要左旋轉成b 圖。
當樹中節點X的右孩子的右孩子上插入新元素,且平衡因子從-1變成-2後,就需要繞節點X進行左旋轉。
2) 繞某元素右旋轉
100 85
/ \ 右旋 / \
85 120 ------ -> 60 100
/ \ \ / \
60 90 80 90 120
\
80
a) B ST樹 b) AVL樹
當樹中節點X的左孩子的左孩子上插入新元素,且平衡因子從1變成2後,就需要繞節點X進行右旋轉。
3) 繞某元素的左子節點左旋轉,接著再繞該元素自己右旋轉。 此情況下就是左旋與右旋 的結合,具體操作時可以分 解成這兩種操作,只是圍繞點不一樣而已。
100 100 90
/ \ 左旋 / \ 右旋 / \
80 120 ------> 90 120 ------> 80 100
/ \ / / \ \
60 90 80 60 85 120
/ / \
85 60 85
當樹中節點X的左孩子的右孩子上插入新元素,且 平衡因子從1變成2後,就需要 先繞X的左子節點Y左旋轉,接著再繞X右旋轉
4) 繞某元素的右子節點右旋轉,接著再繞該元素自己左旋轉。 此情況下就是 右旋與左旋 的結合,具體操作時可以分解 成這兩種操作,只是圍繞點不一樣而已 。
80 80 85
/ \ 右 旋 / \ 左 旋 / \
60 100 ------> 60 85 -------> 80 100
/ \ \ / / \
85 120 100 60 90 120
\ / \
90 90 120
當樹中節點X的右孩子的左孩子上插入新元素,且 平衡因子從-1變成-2後,就需要 先繞X的右子節點Y右旋轉,接著再繞X左旋轉
平衡二叉樹效能分析
平衡二叉樹的效能優勢:
很顯然,平衡二叉樹的優勢在於不會出現普通二叉查詢樹的最差情況。其查詢的時間複雜度為O(logN)。
平衡二叉樹的缺陷:
(1) 很遺憾的是,為了保證高度平衡,動態插入和刪除的代價也隨之增加。因此,我們在下一專題中講講《紅黑樹》 這種更加高效的查詢結構。
(2) 所有二叉查詢樹結構的查詢代價都與樹高是緊密相關的,能否通過減少樹高來進一步降低查詢代價呢。我們可以通過多路查詢樹的結構來做到這一點,在後面專題中我們將通過《多路查詢樹/B-樹/B+樹 》來介紹。
(3) 在大資料量查詢環境下(比如說系統磁盤裡的檔案目錄,資料庫中的記錄查詢 等),所有的二叉查詢樹結構(BST、AVL、RBT)都不合適。如此大規模的資料量(幾G資料),全部組織成平衡二叉樹放在記憶體中是不可能做到的。那麼把這棵樹放在磁碟中吧。問題就來了:假如構造的平衡二叉樹深度有1W層。那麼從根節點出發到葉子節點很可能就需要1W次的硬碟IO讀寫。大家都知道,硬碟的機械部件讀寫資料的速度遠遠趕不上純電子媒體的記憶體。 查詢效率在IO讀寫過程中將會付出巨大的代價。在大規模資料查詢這樣一個實際應用背景下,平衡二叉樹的效率就很成問題了。對這一問題的解決:我們也會在《多路查詢樹/B-樹/B+樹 》 將詳細分析。