資料結構與演算法-二叉查詢樹平衡(AVL)

BackSlash發表於2019-01-02
上節討論的DSW演算法可以從全域性重新平衡樹:每個節點都可能參與樹的重新平衡,或者從節點中移除資料,或者重新設定指標的值。但是,當插入或刪除元素時,將隻影響樹的一部分,此時樹的重新平衡可以只在區域性進行。區域性平衡二叉查詢樹有一個鼎鼎大名的演算法,叫做AVL演算法,由AVL演算法構建出來的二叉樹稱之為AVL樹。
資料結構與演算法-二叉查詢樹平衡(AVL)
AVL樹首先是一顆二叉查詢樹,其次,AVL樹要求每個節點左右子樹的高度差最大為1。例如,圖1-1中的所有樹都是AVL樹。節點中的數字表示平衡因子,即左右子樹的高度差。平衡因子等於右子樹的高度減去左子樹的高度。對於AVL樹,所有的平衡因子都應是+1、0或-1。平衡AVL樹的技術不保證得到的樹是完全平衡的,但卻是高度平衡的。有關平衡的概念可以參考資料結構與演算法-二叉查詢樹平衡(DSW)。
假設我們已經有了一顆AVL樹,那麼如何保證在插入或者刪除元素時繼續保持AVL樹的特性呢?
  • 插入
前人對於AVL樹的插入操作進行總結,發現一共有4種情況需要討論。其中,在左子樹中插入有兩種情況,在右子樹中插入也有兩種情況。並且它們是對稱的,因此,我們以右子樹為例,只需要討論兩種情況即可。
假設我們在AVL樹中插入一個新的節點p,那麼p的父節點以及祖父節點的平衡因子都有可能改變,我們從下到上更新節點的平衡因子,如果發現某個節點的平衡因子成為+2或者-2,那麼就以該節點為根擷取這段子樹。如果根節點平衡因子成為了+2,代表節點插入到了右子樹中,如果根節點平衡因子成為了-2,代表節點插入到了左子樹。我們這裡只討論+2,即節點插入到右子樹上的兩種情況。
如果節點平衡因子變成了+2,那麼,我們可以將這顆子樹抽象出來,它必然是下面這種樣子:
資料結構與演算法-二叉查詢樹平衡(AVL)
也就是說,如果插入新節點使P節點的平衡因子成了+2,那麼以P為根的子樹,必然是上面的樣子。插入的新節點要麼放在Q的左子樹上,要麼放在Q的右子樹上,這就是我們要討論的兩種情況。
1、RR
第一種情況有個簡稱,叫做RR,也就是說新節點插入到右子樹的右節點,就像這樣:
資料結構與演算法-二叉查詢樹平衡(AVL)
怎麼處理呢?P節點的左子樹比右子樹低,還記得左旋的效果嗎?左旋可以提高左子樹的高度,降低右子樹的高度,用在這裡正合適。將Q節點圍繞P節點進行左旋之後效果如下:
資料結構與演算法-二叉查詢樹平衡(AVL)
非常完美,左旋在這裡產生了兩個效果。首先,左旋使當前子樹重新保持平衡,其次,左旋沒有改變當前子樹的高度。左旋之前子樹高度為h + 2,左旋之後依然是h + 2。這就意味著,從Q節點開始往上的節點不需要更新平衡因子了。所以,左旋使得演算法變得足夠簡單。
  • RL
顧名思義,RL的意思是將新節點插入到右子樹的左節點上,就像這樣:
資料結構與演算法-二叉查詢樹平衡(AVL)
怎麼處理呢?首先還是左旋,但是很快你會發現,左旋確實提高了左子樹高度,但是太高了,又造成了新的不平衡,就像這樣:
資料結構與演算法-二叉查詢樹平衡(AVL)
究其原因,是h + 1那顆子樹大大提高了左子樹高度,我們必須把它放在右子樹上才行,可以這樣處理。首先,我們認為,如果是因為在P樹的右子樹的左節點上插入元素導致P節點不平衡,那麼,在沒有插入新節點之前,P樹肯定是下面這種樣子的:
資料結構與演算法-二叉查詢樹平衡(AVL)
Q的左子樹插入了新的節點,可能插入到R的左子樹或者右子樹,先不管這個,為了降低Q的左子樹的高度,將R圍繞著Q進行右旋,結果是這樣:
資料結構與演算法-二叉查詢樹平衡(AVL)
靠譜,R的左子樹高度就是就是m1樹的高度,既可能是h - 1,也可能是h,但是都比h + 1要小,這時我們再做一次左旋,結果如下:
資料結構與演算法-二叉查詢樹平衡(AVL)
完美,R樹最終平衡了,並且R樹的高度依然保持在h + 2,這也意味著不必更新R節點之上的祖先節點的平衡因子了。至於P和Q節點的平衡因子,這要看新節點是插入到m1樹還是m2樹上了,但是無論如何,R樹都已經高度平衡了。
總結一下,對於RL形式的子樹,首先對右子樹的左節點做一次右旋,然後對新的根節點做一次左旋即可。
插入還有LL,LR兩種形式,但是它們分別和RR,RL形式對稱,這裡就不多介紹了。
這裡有個我看到的非常精闢的總結,引用一下:
資料結構與演算法-二叉查詢樹平衡(AVL)
下面介紹刪除節點之後如何平衡二叉查詢樹。
  • 刪除
刪除比插入更加複雜,但是有著插入節點積累的經驗,理解刪除反而更加容易。
刪除節點首先面對的問題是平衡因子從哪個節點開始變化的?這就要說到二叉查詢樹的刪除邏輯了,在資料結構與演算法-二叉查詢樹中有著詳細的介紹。這裡簡單介紹一下,刪除葉子節點或者度為1的節點,平衡因子從被刪除節點的父節點開始變化。刪除度為2的節點需要使用複製刪除的技巧,使用被刪除節點的直接前驅或者直接後繼來替換被刪除節點,然後刪除直接前驅或者直接後繼,那麼,平衡因子就是從直接前驅或者直接後繼的父節點開始變化的。
既然知道了平衡因子開始變化的節點,就可以從下到上的更新平衡因子的變化。
在探討插入節點引起的二叉樹的變化時,我們總結出了RR、RL、LL、LR四種情況,刪除節點會引起怎樣的變化呢?
刪除和插入有兩個不同點:
1、刪除不僅包含以上四中變化,還多出兩種,當然這兩種也是對稱的
資料結構與演算法-二叉查詢樹平衡(AVL)
多出來的兩種是當前節點的父節點的平衡因子等於+2或者-2時,當前節點的平衡因子等於0。
插入節點之所以沒有這兩種變化,是因為插入節點使當前節點平衡因子變成0,說明當前節點的高度沒有變化,更加不會引起祖先節點的平衡因子變化了。而刪除節點時,如果當前節點平衡因子變成0,那麼當前節點的高度其實變小了,可能引起祖先節點的平衡因子變化。
怎麼處理這種情況呢?如果父節點平衡因子等於+2,那就對當前節點進行左旋。如果父節點平衡因子等於-2,那就對當前節點進行右旋即可。
2、重新平衡過程沒有在發現平衡因子為+2或者-2的第一個節點P後停止,而是追蹤到根節點
在插入節點的重新平衡過程中,我們在意的是第一個平衡因子為+2或者-2的節點,原因在於我們執行左旋或者右旋之後,子樹不僅重新平衡並且高度也和沒有插入節點之前保持一致,因此,祖先節點的平衡因子就不會發生變化了。但是刪除節點發生了變化,重新平衡之後子樹的高度變小了。
為什麼重新平衡之後,插入節點高度不變而刪除節點高度變小呢?
可以思考下平衡的過程,假設平衡因子變化為+2的節點為P,它的左子樹高度為h,右子樹高度必然為h + 1,只有在右子樹插入新的節點,P節點的平衡因子才會變化為+2,重新平衡之後該樹的左右子樹高度為h + 1,因此該樹的高度h + 2沒有變化。但是對於刪除又有不同,假設P節點左子樹高度為h,右子樹高度必然為h + 1,只有在左子樹刪除節點之後,P節點的平衡因子才會變化為2,重新平衡之後該樹的左右子樹的高度為h,那麼該樹的高度h + 1比原來就減少了1。因為上述原因,刪除節點之後重新平衡的邏輯不能只執行一次,而是從平衡因子發生變化的節點開始往上一直追蹤到根節點,發現不平衡的節點都要執行平衡邏輯。
到此為止,AVL樹中新增和刪除節點的邏輯已經探討完畢。當然,這裡純粹是學術上的探討,沒有程式碼示例,更加精深的內容需要在實踐中摸索。
知識的積累從來不是看了一兩篇文章就可以完成的,畢竟,功夫在詩外。

資料結構與演算法-自適應二叉樹


相關文章