看圖輕鬆理解資料結構與演算法系列(AVL樹)

超人汪小建發表於2019-03-04

前言

推出一個新系列,《看圖輕鬆理解資料結構和演算法》,主要使用圖片來描述常見的資料結構和演算法,輕鬆閱讀並理解掌握。本系列包括各種堆、各種佇列、各種列表、各種樹、各種圖、各種排序等等幾十篇的樣子。

AVL樹

AVL樹,也稱平衡二叉搜尋樹,AVL是其發明者姓名簡寫。AVL樹屬於樹的一種,而且它也是一棵二叉搜尋樹,不同的是他通過一定機制能保證二叉搜尋樹的平衡,平衡的二叉搜尋樹的查詢效率更高。

AVL樹特點

  • AVL樹是一棵二叉搜尋樹。
  • AVL樹的左右子節點也是AVL樹。
  • AVL樹擁有二叉搜尋樹的所有基本特點。
  • 每個節點的左右子節點的高度之差的絕對值最多為1,即平衡因子為範圍為[-1,1]。

image

圖中紅色數字表示對應節點的高度,可以看到同一層的節點高度差都沒有超過1。

二叉搜尋樹的平衡

基礎的二叉搜尋樹構建出來可能會存在不平衡的現象,比如極端情況下,按照A B C D E F G H順序插入樹中,結果為,

image

但實際上我們更想要平衡一點的二叉搜尋樹,因為平衡的二叉搜尋樹能有效提高查詢效率,比如上面的要查詢“H”節點則需要比較8個節點才找到,而平衡的二叉搜尋樹只需要比較3個節點。

所以AVL樹的出現就是為了解決平衡性問題,它的核心內容就是平衡處理機制,即所謂的旋轉,一共有四種形式的旋轉:右單旋、左單旋、左右雙旋和右左雙旋。

為什麼要旋轉

不管是什麼方式的旋轉,旋轉的目的是為了降低樹的高度,使其平衡,假如樹結構如下圖,

image

將“A”節點新增到樹中,變成如下結構,樹產生了不平衡,於是檢查哪裡不平衡,當到“C”節點時發現高度差超過1,

image

所以需要對“C”節點進行右單旋操作將高度降到2,達到平衡。

image

插入方式

AVL樹一共有四種插入方式,根據插入方式不同需要做不同的旋轉操作,現在往下看四種插入方式,設受插入節點影響而失去平衡的節點的父節點為Z,

  • LL插入方式,插入的節點在Z節點的左子樹的左子樹上,如下圖,“A”節點插入影響“C”節點的平衡,“C”的父節點為“E”,插入節點“A”在“E”節點的左子樹的左子樹上。即“B”節點的左右子節點都算LL插入。

image

  • RR插入方式,插入的節點在Z節點的右子樹的右子樹上,如下圖,“I”節點插入影響“G”節點的平衡,“G”的父節點為“E”,插入節點“I”在“E”節點的右子樹的右子樹上。即“H”節點的左右子節點都算RR插入。

image

  • LR插入方式,插入的節點在Z節點的左子樹的右子樹上,如下圖,“C”節點插入影響“B”節點的平衡,“B”的父節點為“E”,插入節點“C”在“E”節點的左子樹的右子樹上。即“D”節點的左右子節點都算LR插入。

image

  • RL插入方式,插入的節點在Z節點的右子樹的左子樹上,如下圖,“G”節點插入影響“H”節點的平衡,“H”的父節點為“E”,插入節點“G”在“E”節點的右子樹的左子樹上。即“F”節點的左右子節點都算RL插入。

image

右單旋

右單旋用於處理LL插入方式,假設存在一棵樹,如下,

image

現插入“A”節點,假如不進行旋轉的話,樹結構為下圖,所以遍歷過程也會檢查哪裡不平衡,檢查到“C”節點和“G”節點的高度差大於1,而且插入節點“A”屬於“E”節點左子樹的左子樹,於是進行右單旋,

image

“C”節點右單旋即將“C”節點提高,原本它的父節點“E”則變為其右子節點,“C”節點原來的右子節點則變為其父節點“E”的左子節點。右單旋後的結果如下,重新達到了平衡。

image

左單旋

左單旋用於處理RR插入方式,假設存在一棵樹,如下,

image

現插入“I”節點,假如不進行旋轉的話,樹結構為下圖,所以遍歷過程也會檢查哪裡不平衡,檢查到“C”節點和“G”節點的高度差大於1,而且插入節點“I”屬於“E”節點的右子樹的右子樹,於是進行左單旋,

image

“G”節點左單旋即將“G”節點提高,原本它的父節點“E”則變為其左子節點,“G”節點原來的左子節點則變為其父節點“E”的右子節點。左單旋後的結果如下,重新達到了平衡。

image

左右雙旋

左右雙旋用於處理LR插入方式,假設存在一棵樹,如下,

image

現插入“C”節點,假如不進行旋轉的話,樹結構為下圖,遍歷過程會檢查哪裡不平衡,檢查到“B”節點和“G”節點的高度差大於1,而且插入節點“C”屬於“E”節點的左子樹的右子樹,於是進行左右雙旋,

image

先以“D”節點為軸進行左單旋,結果為,

image

再以“D”節點為軸進行右單旋,得到最終結果,

image

右左雙旋

右左雙旋用於處理RL插入方式,假設存在一棵樹,如下,

image

現插入“G”節點,假如不進行旋轉的話,樹結構為下圖,遍歷過程會檢查哪裡不平衡,檢查到“C”節點和“H”節點的高度差大於1,而且插入節點“G”屬於“E”節點的右子樹的左子樹,於是進行右左雙旋,

image

先以“F”節點為軸進行右單旋,結果為,

image

再以“F”節點為軸進行左單旋,得到最終結果,

image

插入

空樹時插入節點“E”直接作為根節點,“E”節點高度設為1,

image

繼續插入“B”節點,小於“E”節點則新增到左邊,且“E”節點高度加1,

image

繼續插入“G”節點,大於“E”節點則新增到右邊,此時“E”節點高度不變,

image

繼續插入“D”節點,最終到“B”節點的右子節點,此時“B”節點高度加1,“E”節點高度也加1,

image

繼續插入“C”節點,最終到“D”節點的左子節點,此時“D”、“B”、“E”節點高度都分別加1,並且先發現節點“D”與它同級節點(不存在即高度為0)高度差大於1,並且屬於RL插入方式,使用右左雙旋處理,

image

以“C”節點為軸進行右單旋,結果為,

image

再以“C”節點為軸進行左單旋,結果如下,可以看到進過右左雙旋操作後二叉樹已經達到平衡了。

image

總結,插入時可能會遇到四種不同的插入方式,分別是:LL插入方式、RR插入方式、LR和RL插入方式。根據不同的插入方式對應做旋轉操作即能使樹達到平衡狀態。

查詢

AVL樹因為屬於二叉搜尋樹,所以查詢時與BST樹完全一樣,比如下面這棵樹,查詢“D”節點,

image

從根節點“C”開始,

image

“D”大於“C”,所以往右繼續查詢,

image

“D”小於“E”,所以往左查詢,找到。

image

刪除

刪除操作主要分兩種情況,一種是刪除後不會影響平衡,那麼直接按照BST樹規則刪除。另外一種是刪除後會影響樹的平衡,那麼則需要再做旋轉處理。

情況一

如樹的結構,要刪除“B”節點,

image

直接找到“B”節點,且因為是葉子節點,直接刪掉即可。

image

最終為,

image

但如果刪除的不是“B”節點,而是“C”節點,則不能直接刪除“C”節點,

image

應該先找到“C”節點的前驅,它的前驅為“B”節點,使用“B”替換“C”節點,

image

最後將原來的“B”節點刪除。

image

情況二

如樹的結構,要刪除“F”節點,

image

先找到“F”節點,

image

然後將“F”節點刪除,此時導致了“C”節點和“G”節點的高度差超過1,需要做旋轉操作,

image

而且因為C節點的左子節點高度比右子節點高度大,所以執行右單旋操作,旋轉後為,

image

-------------推薦閱讀------------

我的開源專案彙總(機器&深度學習、NLP、網路IO、AIML、mysql協議、chatbot)

為什麼寫《Tomcat核心設計剖析》

我的2017文章彙總——機器學習篇

我的2017文章彙總——Java及中介軟體

我的2017文章彙總——深度學習篇

我的2017文章彙總——JDK原始碼篇

我的2017文章彙總——自然語言處理篇

我的2017文章彙總——Java併發篇


跟我交流,向我提問:

看圖輕鬆理解資料結構與演算法系列(AVL樹)

歡迎關注:

看圖輕鬆理解資料結構與演算法系列(AVL樹)

相關文章