前言
推出一個新系列,《看圖輕鬆理解資料結構和演算法》,主要使用圖片來描述常見的資料結構和演算法,輕鬆閱讀並理解掌握。本系列包括各種堆、各種佇列、各種列表、各種樹、各種圖、各種排序等等幾十篇的樣子。
AVL樹
AVL樹,也稱平衡二叉搜尋樹,AVL是其發明者姓名簡寫。AVL樹屬於樹的一種,而且它也是一棵二叉搜尋樹,不同的是他通過一定機制能保證二叉搜尋樹的平衡,平衡的二叉搜尋樹的查詢效率更高。
AVL樹特點
- AVL樹是一棵二叉搜尋樹。
- AVL樹的左右子節點也是AVL樹。
- AVL樹擁有二叉搜尋樹的所有基本特點。
- 每個節點的左右子節點的高度之差的絕對值最多為1,即平衡因子為範圍為[-1,1]。

圖中紅色數字表示對應節點的高度,可以看到同一層的節點高度差都沒有超過1。
二叉搜尋樹的平衡
基礎的二叉搜尋樹構建出來可能會存在不平衡的現象,比如極端情況下,按照A B C D E F G H
順序插入樹中,結果為,

但實際上我們更想要平衡一點的二叉搜尋樹,因為平衡的二叉搜尋樹能有效提高查詢效率,比如上面的要查詢“H”節點則需要比較8個節點才找到,而平衡的二叉搜尋樹只需要比較3個節點。
所以AVL樹的出現就是為了解決平衡性問題,它的核心內容就是平衡處理機制,即所謂的旋轉,一共有四種形式的旋轉:右單旋、左單旋、左右雙旋和右左雙旋。
為什麼要旋轉
不管是什麼方式的旋轉,旋轉的目的是為了降低樹的高度,使其平衡,假如樹結構如下圖,

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

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

插入方式
AVL樹一共有四種插入方式,根據插入方式不同需要做不同的旋轉操作,現在往下看四種插入方式,設受插入節點影響而失去平衡的節點的父節點為Z,
- LL插入方式,插入的節點在Z節點的左子樹的左子樹上,如下圖,“A”節點插入影響“C”節點的平衡,“C”的父節點為“E”,插入節點“A”在“E”節點的左子樹的左子樹上。即“B”節點的左右子節點都算LL插入。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

總結,插入時可能會遇到四種不同的插入方式,分別是:LL插入方式、RR插入方式、LR和RL插入方式。根據不同的插入方式對應做旋轉操作即能使樹達到平衡狀態。
查詢
AVL樹因為屬於二叉搜尋樹,所以查詢時與BST樹完全一樣,比如下面這棵樹,查詢“D”節點,

從根節點“C”開始,

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

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

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

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

最終為,

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

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

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

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

先找到“F”節點,

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

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

-------------推薦閱讀------------
我的開源專案彙總(機器&深度學習、NLP、網路IO、AIML、mysql協議、chatbot)
跟我交流,向我提問:

歡迎關注:
