一、AVL 樹
在計算機科學中,AVL樹是最早被發明的自平衡二叉查找樹。在AVL樹中,任一節點對應的兩棵子樹的最大高度差為 1,因此它也被稱為高度平衡樹。查找、插入和刪除在平均和最壞情況下的時間復雜度都是 O(log(n))。插入和刪除元素的操作則可能需要借由一次或多次樹旋轉,以實現樹的重新平衡。
節點的平衡因子是它的左子樹的高度減去它的右子樹的高度(有時相反)。帶有平衡因子 1、0 或 -1 的節點被認為是平衡的。帶有平衡因子 -2 或 2 的節點被認為是不平衡的,並需要重新平衡這個樹。平衡因子可以直接存儲在每個節點中,或從可能存儲在節點中的子樹高度計算出來。
大多數 BST 操作(例如,搜索,最大,最小,插入,刪除等)花費 O(h) 時間,其中 h 是 BST 的高度。對於偏斜的二叉樹,這些操作的成本可能變為 O(n)。如果確保每次插入和刪除后樹的高度都保持 O(log2n),則可以保證所有這些操作的 O(log2n)上限。AVL樹的高度始終為 O(log2n),其中 n 是樹中的節點數。
二、AVL 樹的旋轉
AVL 樹在普通的插入和刪除節點時都會使得樹失去平衡,這時我們需要一些操作來把樹恢復平衡,這些操作叫做AVL樹的旋轉,分為左旋和右旋。
T1,T2 和 T3 是樹 y(左邊) 或 x(右邊) 的子樹:
y x / \ Right Rotation / \ x T3 - - - - - - - > T1 y / \ < - - - - - - - / \ T1 T2 Left Rotation T2 T3
以上兩個樹中的鍵都遵循以下順序(二叉查找樹的性質):
keys(T1) < key(x) < keys(T2) < key(y) < keys(T3)。
1 /** 2 * 右旋轉以y為根的子樹 3 * 4 * @param y 5 * @return 6 */ 7 private Node rightRoate(Node y) { 8 Node x = y.left; 9 Node T2 = x.right; 10 11 /* 執行旋轉 */ 12 x.right = y; 13 y.left = T2; 14 15 /* 更新高度 */ 16 y.height = max(height(y.left), height(y.right)) + 1; 17 x.height = max(height(x.left), height(x.right)) + 1; 18 19 return x; 20 } 21 22 /** 23 * 左旋轉以x為根的子樹 24 * 25 * @param x 26 * @return 27 */ 28 private Node leftRoate(Node x) { 29 Node y = x.right; 30 Node T2 = y.left; 31 32 /* 執行旋轉 */ 33 y.left = x; 34 x.right = T2; 35 36 /* 更新高度 */ 37 x.height = max(height(x.left), height(x.right)) + 1; 38 y.height = max(height(y.left), height(y.right)) + 1; 39 40 return y; 41 }
三、AVL 樹的插入操作
插入要遵循的步驟:
新插入的節點為 w
1)對 w 執行標准 BST 插入。
2)從 w 開始,向上移動並找到第一個不平衡節點。令 z 為第一個不平衡節點,y 為從 w 到 z 的路徑中 z 的子代,x 為從 w 到 z 的路徑中 z 的孫代。
3)通過對以 z 為根的子樹執行適當的旋轉來重新平衡樹。可能有 4 種可能的情況需要處理,因為 x,y 和 z 可以 4 種方式排列。以下是可能的 4 種排列方式:
a)y 是 z 的左子代,x 是 y 的左子代(左案例)
z y / \ / \ y T4 Right Rotate (z) x z / \ - - - - - - - - -> / \ / \ x T3 T1 T2 T3 T4 / \ T1 T2
b)y 是 z 的左子代,x 是 y 的右子代(左案例)
z z x / \ / \ / \ y T4 Left Rotate (y) x T4 Right Rotate(z) y z / \ - - - - - - - - -> / \ - - - - - - - -> / \ / \ T1 x y T3 T1 T2 T3 T4 / \ / \ T2 T3 T1 T2
c)y 是 z 的右子代,x 是 y 的右子代(右右案例)
z y / \ / \ T1 y Left Rotate(z) z x / \ - - - - - - - -> / \ / \ T2 x T1 T2 T3 T4 / \ T3 T4
d)y 是 z 的右子代,x 是 y 的左子代(右左案例)
z z x / \ / \ / \ T1 y Right Rotate (y) T1 x Left Rotate(z) z y / \ - - - - - - - - -> / \ - - - - - - - -> / \ / \ x T4 T2 y T1 T2 T3 T4 / \ / \ T2 T3 T3 T4
插入操作:
1 /** 2 * AVL樹的插入 3 * 4 * @param node 5 * @param key 6 * @return 7 */ 8 private Node insert(Node node, int key) { 9 /* 執行正常的BST插入 */ 10 if (node == null) 11 return (new Node(key)); 12 13 if (key < node.key) 14 node.left = insert(node.left, key); 15 else if (key > node.key) 16 node.right = insert(node.right, key); 17 else // 不允許重復的key 18 return node; 19 20 /* 更新此祖先節點的高度 */ 21 node.height = 1 + max(height(node.left), height(node.right)); 22 23 /* 獲取此祖先的平衡因子以檢查此節點是否變為不平衡 */ 24 int balance = getBalance(node); 25 26 /* 如果此節點變得不平衡,則存在有4種情況 */ 27 if (balance > 1 && key < node.left.key) { 28 return rightRoate(node); 29 } 30 31 if (balance < -1 && key > node.right.key) { 32 return leftRoate(node); 33 } 34 35 if (balance > 1 && key > node.left.key) { 36 node.left = leftRoate(node.left); 37 return rightRoate(node); 38 } 39 40 if (balance < -1 && key < node.right.key) { 41 node.right = rightRoate(node.right); 42 return leftRoate(node); 43 } 44 45 return node; 46 }
四、AVL 樹的刪除操作
刪除要遵循的步驟:
令 w 為要刪除的節點
1)對 w 執行標准BST刪除。
2)從 w 開始,向上移動並找到第一個不平衡節點。令 z 為第一個不平衡節點,y 為 z 的較大孩子,x 為 y 的較大孩子。請注意,x 和 y 的定義與此處的插入不同。
3)通過對以 z 為根的子樹執行適當的旋轉來重新平衡樹。有 4 種可能的情況需要處理,因為 x,y 和 z 可以 4 種方式排列。以下是可能的 4 種排列方式:
a)y 是 z 的左子代,x是y的左子代(左案例)
z y / \ / \ y T4 Right Rotate (z) x z / \ - - - - - - - - -> / \ / \ x T3 T1 T2 T3 T4 / \ T1 T2
b)y 是 z 的左子代,x 是 y 的右子代(左案例)
z z x / \ / \ / \ y T4 Left Rotate (y) x T4 Right Rotate(z) y z / \ - - - - - - - - -> / \ - - - - - - - -> / \ / \ T1 x y T3 T1 T2 T3 T4 / \ / \ T2 T3 T1 T2
c)y 是 z 的右子代,x 是 y 的右子代(右右案例)
z y / \ / \ T1 y Left Rotate(z) z x / \ - - - - - - - -> / \ / \ T2 x T1 T2 T3 T4 / \ T3 T4
d)y 是 z 的右子代,x 是 y 的左代子(右左案例)
z z x / \ / \ / \ T1 y Right Rotate (y) T1 x Left Rotate(z) z y / \ - - - - - - - - -> / \ - - - - - - - -> / \ / \ x T4 T2 y T1 T2 T3 T4 / \ / \ T2 T3 T3 T4
與插入不同,在刪除中,在 z 處進行旋轉后,可能必須在 z 的祖先處進行旋轉。因此,我們必須繼續追蹤路徑,直到到達根為止。
刪除操作:
1 /** 2 * AVL樹的刪除 3 * 4 * @param N 5 * @return 6 */ 7 private Node deleteNode(Node root, int key) { 8 if (root == null) 9 return root; 10 /* 如果要刪除的key小於root的key,則它位於左子樹中 */ 11 if (key < root.key) 12 root.left = deleteNode(root.left, key); 13 14 /* 如果要刪除的key大於root的key,則它位於右子樹中 */ 15 else if (key > root.key) 16 root.right = deleteNode(root.right, key); 17 18 /* 如果key與root的key相同,則這個節點要被刪除 */ 19 else { 20 /* 只有一個孩子或沒有孩子的節點 */ 21 if ((root.left == null) || (root.right == null)) { 22 Node temp = null; 23 if (temp == root.left) 24 temp = root.right; 25 else 26 temp = root.left; 27 28 /* 沒有孩子的情況 */ 29 if (temp == null) { 30 temp = root; 31 root = null; 32 } else { // 只有一個孩子 33 root = temp; // 復制非空孩子的內容 34 } 35 36 } else { 37 /* 有兩個子節點的節點:獲取后繼節點(在右側子樹中最小) */ 38 Node temp = minValueNode(root.right); 39 /* 將后繼節點的數據復制到此節點 */ 40 root.key = temp.key; 41 /* 刪除后繼節點 */ 42 root.right = deleteNode(root.right, temp.key); 43 } 44 } 45 46 /* 如果樹只有一個節點,則返回 */ 47 if (root == null) 48 return root; 49 50 /* 更新當前節點的高度 */ 51 root.height = max(height(root.left), height(root.right)) + 1; 52 /* 獲取此節點的平衡因素 */ 53 int balance = getBalance(root); 54 55 /* 如果此節點變得不平衡,則有4種情況 */ 56 if (balance > 1 && getBalance(root.left) >= 0) { 57 return rightRoate(root); 58 } 59 60 if (balance > 1 && getBalance(root.left) < 0) { 61 root.left = leftRoate(root.left); 62 return rightRoate(root); 63 } 64 65 if (balance < -1 && getBalance(root.right) <= 0) { 66 return leftRoate(root); 67 } 68 69 if (balance < -1 && getBalance(root.right) > 0) { 70 root.right = rightRoate(root.right); 71 return leftRoate(root); 72 } 73 74 return root; 75 }
本文源代碼:

1 package algorithm; 2 3 /** 4 * 自平衡二叉樹,左右子樹的高度差不大於1 5 */ 6 public class AVLTree { 7 8 private Node root; 9 10 /** 11 * 樹高度 12 * 13 * @param N 14 * @return 15 */ 16 private int height(Node N) { 17 if (N == null) { 18 return 0; 19 } 20 return N.height; 21 } 22 23 private int max(int a, int b) { 24 return Math.max(a, b); 25 } 26 27 /** 28 * 右旋轉以y為根的子樹 29 * 30 * @param y 31 * @return 32 */ 33 private Node rightRoate(Node y) { 34 Node x = y.left; 35 Node T2 = x.right; 36 37 /* 執行旋轉 */ 38 x.right = y; 39 y.left = T2; 40 41 /* 更新高度 */ 42 y.height = max(height(y.left), height(y.right)) + 1; 43 x.height = max(height(x.left), height(x.right)) + 1; 44 45 return x; 46 } 47 48 /** 49 * 左旋轉以x為根的子樹 50 * 51 * @param x 52 * @return 53 */ 54 private Node leftRoate(Node x) { 55 Node y = x.right; 56 Node T2 = y.left; 57 58 /* 執行旋轉 */ 59 y.left = x; 60 x.right = T2; 61 62 /* 更新高度 */ 63 x.height = max(height(x.left), height(x.right)) + 1; 64 y.height = max(height(y.left), height(y.right)) + 1; 65 66 return y; 67 } 68 69 /** 70 * 獲取N結點的平衡 71 * 72 * @param N 73 * @return 74 */ 75 private int getBalance(Node N) { 76 if (N == null) 77 return 0; 78 79 return height(N.left) - height(N.right); 80 } 81 82 /** 83 * AVL樹的插入 84 * 85 * @param node 86 * @param key 87 * @return 88 */ 89 private Node insert(Node node, int key) { 90 /* 執行正常的BST插入 */ 91 if (node == null) 92 return (new Node(key)); 93 94 if (key < node.key) 95 node.left = insert(node.left, key); 96 else if (key > node.key) 97 node.right = insert(node.right, key); 98 else // 不允許重復的key 99 return node; 100 101 /* 更新此祖先節點的高度 */ 102 node.height = 1 + max(height(node.left), height(node.right)); 103 104 /* 獲取此祖先的平衡因子以檢查此節點是否變為不平衡 */ 105 int balance = getBalance(node); 106 107 /* 如果此節點變得不平衡,則存在有4種情況 */ 108 if (balance > 1 && key < node.left.key) { 109 return rightRoate(node); 110 } 111 112 if (balance < -1 && key > node.right.key) { 113 return leftRoate(node); 114 } 115 116 if (balance > 1 && key > node.left.key) { 117 node.left = leftRoate(node.left); 118 return rightRoate(node); 119 } 120 121 if (balance < -1 && key < node.right.key) { 122 node.right = rightRoate(node.right); 123 return leftRoate(node); 124 } 125 126 return node; 127 } 128 129 /** 130 * 給定一個非空的二叉查找樹,返回樹中最小key值的結點(注意無需遍歷整個樹) 131 * 132 * @param node 133 * @return 134 */ 135 private Node minValueNode(Node node) { 136 Node current = node; 137 138 while (current.left != null) 139 current = current.left; 140 141 return current; 142 } 143 144 /** 145 * AVL樹的刪除 146 * 147 * @param N 148 * @return 149 */ 150 private Node deleteNode(Node root, int key) { 151 if (root == null) 152 return root; 153 /* 如果要刪除的key小於root的key,則它位於左子樹中 */ 154 if (key < root.key) 155 root.left = deleteNode(root.left, key); 156 157 /* 如果要刪除的key大於root的key,則它位於右子樹中 */ 158 else if (key > root.key) 159 root.right = deleteNode(root.right, key); 160 161 /* 如果key與root的key相同,則這個節點要被刪除 */ 162 else { 163 /* 只有一個孩子或沒有孩子的節點 */ 164 if ((root.left == null) || (root.right == null)) { 165 Node temp = null; 166 if (temp == root.left) 167 temp = root.right; 168 else 169 temp = root.left; 170 171 /* 沒有孩子的情況 */ 172 if (temp == null) { 173 temp = root; 174 root = null; 175 } else { // 只有一個孩子 176 root = temp; // 復制非空孩子的內容 177 } 178 179 } else { 180 /* 有兩個子節點的節點:獲取后繼節點(在右側子樹中最小) */ 181 Node temp = minValueNode(root.right); 182 /* 將后繼節點的數據復制到此節點 */ 183 root.key = temp.key; 184 /* 刪除后繼節點 */ 185 root.right = deleteNode(root.right, temp.key); 186 } 187 } 188 189 /* 如果樹只有一個節點,則返回 */ 190 if (root == null) 191 return root; 192 193 /* 更新當前節點的高度 */ 194 root.height = max(height(root.left), height(root.right)) + 1; 195 /* 獲取此節點的平衡因素 */ 196 int balance = getBalance(root); 197 198 /* 如果此節點變得不平衡,則有4種情況 */ 199 if (balance > 1 && getBalance(root.left) >= 0) { 200 return rightRoate(root); 201 } 202 203 if (balance > 1 && getBalance(root.left) < 0) { 204 root.left = leftRoate(root.left); 205 return rightRoate(root); 206 } 207 208 if (balance < -1 && getBalance(root.right) <= 0) { 209 return leftRoate(root); 210 } 211 212 if (balance < -1 && getBalance(root.right) > 0) { 213 root.right = rightRoate(root.right); 214 return leftRoate(root); 215 } 216 217 return root; 218 } 219 220 /** 221 * 先序遍歷 222 * 223 * @param node 224 */ 225 private void preOrder(Node node) { 226 if (node != null) { 227 System.out.print(node.key + " "); 228 preOrder(node.left); 229 preOrder(node.right); 230 } 231 } 232 233 public static void main(String[] args) { 234 235 /* ---------------------------------------------------- */ 236 237 AVLTree tree = new AVLTree(); 238 239 /* 構造AVL樹 */ 240 tree.root = tree.insert(tree.root, 9); 241 tree.root = tree.insert(tree.root, 5); 242 tree.root = tree.insert(tree.root, 10); 243 tree.root = tree.insert(tree.root, 0); 244 tree.root = tree.insert(tree.root, 6); 245 tree.root = tree.insert(tree.root, 11); 246 tree.root = tree.insert(tree.root, -1); 247 tree.root = tree.insert(tree.root, 1); 248 tree.root = tree.insert(tree.root, 2); 249 250 /* 構造的AVL樹: 251 9 252 / \ 253 1 10 254 / \ \ 255 0 5 11 256 / / \ 257 -1 2 6 258 */ 259 System.out.println("Preorder traversal of constructed tree is : "); 260 tree.preOrder(tree.root); 261 262 tree.root = tree.deleteNode(tree.root, 10); 263 264 /* 刪除10后的AVL樹: 265 1 266 / \ 267 0 9 268 / / \ 269 -1 5 11 270 / \ 271 2 6 272 */ 273 System.out.println(); 274 System.out.println("Preorder traversal after "+ 275 "deletion of 10 :"); 276 tree.preOrder(tree.root); 277 } 278 } 279 280 class Node { 281 int key, height; 282 Node left, right; 283 284 Node(int d) { 285 key = d; 286 height = 1; 287 } 288 289 }