# [資料結構] CH8. AVL Trees ## 簡介 * AVL Trees是屬於Binary Search Trees的一種,也就是說,它必須符合Binary Search Trees的所有特性。 * 除了Binary Search Trees的特性外,AVL還必須符合以下特性: ``` ※每一個node的左子樹高度-右子樹高度只能是-1、0、1。``` * 因為上述特性,AVL Trees是一種**平衡樹**。 * 由於整個樹不會長歪,在搜尋時的速度會更快。 * 一個AVL的例子: ```graphviz digraph AVL{ 1 [label="45"]; 2 [label="20"]; 3 [label="66"]; 4 [label="18"]; 5 [label="22"]; 6 [label="50"]; 7 [label="67"]; 8 [label="99"]; 1 -> 2 1 -> 3 2 -> 4 2 -> 5 3 -> 6 3 -> 7 7 -> 8 } ``` ## 實作AVL Trees * 為了維持樹的平衡,在插入、刪除時需要進行調整。 ### 插入 ``` 1.照一般Binary Search Trees的方法插入資料。 2.向上檢查高度是否符合AVL限制。 3.如果符合,結束。 4.如果不符合,旋轉。 ``` * 前面的插入因為同前面章節的insert,故跳過。 * 從你插入資料的node開始檢查,如果到head node都沒問題就OK。 * 如發現該節點的子樹高度差>1,請往下找兩層,並根據這兩層的左右方向,進行LL/RR/LR/RL旋轉。 #### LL旋轉 * 下圖是一個LL旋轉的例子;插入一個新資料"6"之後,導致樹的不平衡。 * 經過向上檢查,18 OK、36 OK,然而在45的地方發現高度差大於一了。 * 檢查路徑後,因為走的路徑是左子樹+左子樹,判定該狀況為LL旋轉。 ```graphviz digraph AVL{ 1 [label="45",color="red"]; 2 [label="36"]; 3 [label="63"]; 4 [label="18"]; 5 [label="22"]; 6 [label="6(new data)"]; 1 -> 2[label="L"] 1 -> 3 2 -> 4[label="L"] 2 -> 5 4 -> 6 } ``` * 觀察一下45、36、18三個node,因為路徑為LL,所以代表45的位置一定最大,36其次,18最小。我們要做的事情就是**將中間值往上提,較小較大放左右**。 * 如此一來,便完成了AVL了平衡。 ```graphviz digraph LL{ subgraph cluster_level1 { 1 [label="A"]; 2 [label="B"]; 3 [label="T3",shape="box"]; 4 [label="T1(new data here)",shape="box"]; 5 [label="T2",shape="box"]; } 2 -> 1[style="dashed",color="orange"] 4 -> 2[style="dashed",color="orange"] 1 -> 2[label="L"] 1 -> 3 2 -> 4[label="L"] 2 -> 5 1 -> 3[style="dashed",color="orange"] subgraph cluster_level2 { 22 [label="B"]; 23 [label="T1",shape="box"]; 21 [label="A"]; 24 [label="T2",shape="box"]; 25 [label="T3",shape="box"]; } 22 -> 21 22 -> 23 21 -> 24 21 -> 25 } ``` #### RR、LR、RL旋轉 * 由於概念與上面的LL相似,在此就只簡單圖片表示。 * 特別注意,我們都是**將中間值往上提,較小較大放左右**(因為很重要再說一次)。 - RR ```graphviz digraph RR{ subgraph cluster_level1 { 1 [label="A"]; 3 [label="T1",shape="box"]; 2 [label="B"]; 4 [label="T2",shape="box"]; 5 [label="T3 (new data here)",shape="box"]; } 1 -> 2[label="R"] 1 -> 3 2 -> 4 2 -> 5[label="R"] 5 -> 2[style="dashed",color="orange"] 2 -> 1[style="dashed",color="orange"] 1 -> 3[style="dashed",color="orange"] subgraph cluster_level2 { 22 [label="B"]; 21 [label="A"]; 23 [label="T3",shape="box"]; 24 [label="T1",shape="box"]; 25 [label="T2",shape="box"]; } 22 -> 21 22 -> 23 21 -> 24 21 -> 25 } ``` - LR - A,B,C中,C是中間值! ```graphviz digraph LR{ subgraph cluster_level1 { 1 [label="A"]; 2 [label="B"]; 4 [label="T1",shape="box"]; 5 [label="T2 (new data here)",shape="box"]; 3 [label="C"]; 6 [label="T3",shape="box"]; 7 [label="T4",shape="box"]; } 1 -> 2[label="L"] 1 -> 7 2 -> 4 2 -> 3[label="R"] 3 -> 5 3 -> 6 3 -> 1[style="dashed",color="orange"] 1 -> 7[style="dashed",color="orange"] subgraph cluster_level2 { 22 [label="B"]; 21 [label="A"]; 24 [label="T1",shape="box"]; 25 [label="T2",shape="box"]; 23 [label="C"]; 26 [label="T3",shape="box"]; 27 [label="T4",shape="box"]; } 23->21 23->22 22->24 22->25 21->26 21->27 } ``` - RL - A,B,C中,C是中間值! - 該繪圖語言的某些關係,這張圖我畫不出虛線。 ```graphviz digraph RL{ subgraph cluster_level1 { 1 [label="A"]; 7 [label="T1",shape="box"]; 2 [label="B"]; 3 [label="C"]; 4 [label="T4",shape="box"]; 5 [label="T2(new data here)",shape="box"]; 6 [label="T3",shape="box"]; } 1 -> 2[label="R"] 1 -> 7 2 -> 3[label="L"] 2 -> 4 3 -> 5 3 -> 6 subgraph cluster_level2 { 21 [label="A"]; 22 [label="B"]; 24 [label="T3",shape="box"]; 25 [label="T4",shape="box"]; 23 [label="C"]; 26 [label="T1",shape="box"]; 27 [label="T2",shape="box"]; } 23->21 23->22 22->24 22->25 21->26 21->27 } ``` ### 刪除 * 刪除掉元素一樣會導致樹不平衡,我們一樣利用旋轉來維持平衡。 ``` 1.照一般Binary Search Trees的規則去刪除。 2.找任意樹葉往上檢查左右子樹高度差。 3.如果都符合,結束。 4.不符合,旋轉。 5.再次檢查,如不符合,再次旋轉。 ``` * 旋轉的方法都和上面插入時的一樣,故不討論。 * 特別注意,根據選擇的樹葉不同,可能導致旋轉完之後依然不平衡。 * 我不確定這邊應該回到旋轉前的樣子,選擇其他樹葉旋轉,還是直接再次旋轉。 * 因為畫那些圖很累,我不想畫例子了。 ###### tags: `DS` `note`