AVL 平衡树和树旋转
目录
AVL(Adelson-Velskii & Landis)树是一种带有平衡条件的二叉树,一棵AVL树其实是一棵左子树和右子树高度最多差1的二叉查找树。一棵树的不平衡主要是由于插入和删除的过程中产生的,此时则需要使用旋转来对AVL树进行平衡。
AVL Tree: 0 _____|_____ | | 0 0 |___ ___|___ | | | 0 0 0 |__ | 0
插入引起不平衡主要有以下四种情况:
Insert unbalance: 1: | 2: | 3: | 4: A(2) | A(2) | A(2) | A(2) __| | __| | |__ | |__ | | | | | | | B(1) | B(1) | B(1) | B(1) __| | |__ | __| | |__ | | | | | | | ---- | ---- | ---- | ---- |X(0)| | |X(0)| | |X(0)| | |X(0)| ---- | ---- | ---- | ----
删除引起不平衡主要有以下四种情况:
Delete unbalance: 1: | 2: | 3: | 4: A(2) | A(2) | A(2) | A(2) __|__ | __|__ | __|__ | __|__ | | | | | | | | | | | B(1) ---- | B(1) ---- | ---- B(1) | ---- B(1) __| |X(0)| | |__ |X(0)| | |X(0)| __| | |X(0)| |__ | ---- | | ---- | ---- | | ---- | C(0) | C(0) | C(0) | C(0)
对于上面的不平衡情况,可以分别采用以下的树旋转方式解决,
2.1 向左单旋转
适用于处理上面插入/删除引起的不平衡情况1,
下图中K2两个子树节点相差高度等于2,产生了不平衡,因此需要使用单旋转处理,
在向左单旋转时,若K1有右子树,则转变为K2的左子树。
K2(2) K1(1) __|__ ____|_____ | | | | K1(1) None(-1) --------> X(0) K2(1) __|__ __|__ | | | | X(0) Y(0) Y(0) None(-1)
2.2 向右单旋转
适用于处理上面插入/删除引起的不平衡情况4,
在向右单旋转时,若K1有左子树,则转变为K2的右子树。
K2(2) K1(2) __|__ ____|_____ | | | | (-1)None K1(1) --------> K2(1) Y(0) __|__ __|__ | | | | X(0) Y(0) (-1)None X(0)
2.3 右左双旋转
适用于处理上面插入/删除引起的不平衡情况2,
首先对K1进行一次向右单旋转,再对K3进行一次向左单旋转,
K3(3) K3(3) K2(2) __|__ __|__ _____|_____ | | right | | left | | K1(2) D(0) --------> K2(2) D(0) --------> K1(1) K3(1) __|__ __|__ __|__ __|__ | | | | | | | | A(0) K2(1) K1(1) C(0) A(0) B(0) C(0) D(0) __|__ __|__ | | | | B(0) C(0) A(0) B(0)
2.4 左右双旋转
适用于处理上面插入/删除引起的不平衡情况3,
首先对K1进行一次向左单旋转,再对K3进行一次向右单旋转,
K3(3) K3(3) K2(2) __|__ __|__ _____|_____ | | left | | right | | D(0) K1(2) --------> D(0) K2(2) --------> K3(1) K1(1) __|__ __|__ __|__ __|__ | | | | | | | | K2(1) A(0) C(0) K1(1) D(0) C(0) B(0) A(0) __|__ __|__ | | | | C(0) B(0) B(0) A(0)
下面利用Python实现AVL树,以及树的旋转操作,
完整代码
1 from search_tree import SearchTree 2 3 4 class AvlNode: 5 def __init__(self, val=None, lef=None, rgt=None, hgt=0): 6 self.value = val 7 self.left = lef 8 self.right = rgt 9 self.height = hgt 10 11 def __str__(self): 12 return str(self.value) 13 14 15 class AvlTree(SearchTree): 16 """ 17 AVL Tree: 18 0 19 _____|_____ 20 | | 21 0 0 22 |___ ___|___ 23 | | | 24 0 0 0 25 |__ 26 | 27 0 28 Insert unbalance: 29 1: | 2: | 3: | 4: 30 A(2) | A(2) | A(2) | A(2) 31 __| | __| | |__ | |__ 32 | | | | | | | 33 B(1) | B(1) | B(1) | B(1) 34 __| | |__ | __| | |__ 35 | | | | | | | 36 ---- | ---- | ---- | ---- 37 |X(0)| | |X(0)| | |X(0)| | |X(0)| 38 ---- | ---- | ---- | ---- 39 40 Delete unbalance: 41 1: | 2: | 3: | 4: 42 A(2) | A(2) | A(2) | A(2) 43 __|__ | __|__ | __|__ | __|__ 44 | | | | | | | | | | | 45 B(1) ---- | B(1) ---- | ---- B(1) | ---- B(1) 46 __| |X(0)| | |__ |X(0)| | |X(0)| __| | |X(0)| |__ 47 | ---- | | ---- | ---- | | ---- | 48 C(0) | C(0) | C(0) | C(0) 49 """ 50 def get_height(self, node): 51 if isinstance(node, AvlNode): 52 return node.height 53 return -1 54 55 def single_rotate_left(self, k2): 56 """ 57 K2(2) K1(1) 58 __|__ ____|_____ 59 | | | | 60 K1(1) None(-1) --------> X(0) K2(1) 61 __|__ __|__ 62 | | | | 63 X(0) Y(0) Y(0) None(-1) 64 """ 65 # Left rotate 66 k1 = k2.left 67 k2.left = k1.right 68 k1.right = k2 69 # Update height 70 k2.height = max(self.get_height(k2.left), self.get_height(k2.right)) + 1 71 k1.height = max(self.get_height(k1.left), k2.height) + 1 72 return k1 73 74 def single_rotate_right(self, k2): 75 """ 76 K2(2) K1(2) 77 __|__ ____|_____ 78 | | | | 79 (-1)None K1(1) --------> K2(1) Y(0) 80 __|__ __|__ 81 | | | | 82 X(0) Y(0) (-1)None X(0) 83 """ 84 # Right rotate 85 k1 = k2.right 86 k2.right = k1.left 87 k1.left = k2 88 # Update height 89 k2.height = max(self.get_height(k2.left), self.get_height(k2.right)) + 1 90 k1.height = max(k2.height, self.get_height(k1.right)) + 1 91 return k1 92 93 def double_rotate_left(self, k3): 94 """ 95 K3(3) K3(3) K2(2) 96 __|__ __|__ _____|_____ 97 | | right | | left | | 98 K1(2) D(0) --------> K2(2) D(0) --------> K1(1) K3(1) 99 __|__ __|__ __|__ __|__ 100 | | | | | | | | 101 A(0) K2(1) K1(1) C(0) A(0) B(0) C(0) D(0) 102 __|__ __|__ 103 | | | | 104 B(0) C(0) A(0) B(0) 105 """ 106 # Rotate between k1 and k2 107 k3.left = self.single_rotate_right(k3.left) 108 # Rotate between k3 and k2 109 return self.single_rotate_left(k3) 110 111 def double_rotate_right(self, k3): 112 """ 113 K3(3) K3(3) K2(2) 114 __|__ __|__ _____|_____ 115 | | left | | right | | 116 D(0) K1(2) --------> D(0) K2(2) --------> K3(1) K1(1) 117 __|__ __|__ __|__ __|__ 118 | | | | | | | | 119 K2(1) A(0) C(0) K1(1) D(0) C(0) B(0) A(0) 120 __|__ __|__ 121 | | | | 122 C(0) B(0) B(0) A(0) 123 """ 124 # Rotate between k1 and k2 125 k3.right = self.single_rotate_left(k3.right) 126 # Rotate between k3 and k2 127 return self.single_rotate_right(k3) 128 129 def insert(self, item): 130 if self._root is None: 131 self._root = AvlNode(item) 132 return 133 134 def _insert(item, node): 135 if not node: 136 return AvlNode(item) 137 if item < node.value: 138 node.left = _insert(item, node.left) 139 if self.get_height(node.left) - self.get_height(node.right) == 2: 140 if item < node.left.value: # Situation 1: left --> left 141 node = self.single_rotate_left(node) 142 else: # Situation 2: left --> right 143 node = self.double_rotate_left(node) 144 elif item > node.value: 145 node.right = _insert(item, node.right) 146 if self.get_height(node.right) - self.get_height(node.left) == 2: 147 if item < node.right.value: # Situation 3: right --> left 148 node = self.double_rotate_right(node) 149 else: # Situation 4: right --> right 150 node = self.single_rotate_right(node) 151 else: pass 152 # Update node height (if not rotated, update is necessary.) 153 node.height = max(self.get_height(node.left), self.get_height(node.right)) + 1 154 return node 155 self._root = _insert(item, self._root) 156 157 def delete(self, item): 158 if self._root is None: 159 return 160 161 def _delete(item, node): 162 if not node: # Node no found 163 # return None 164 raise Exception('Element not in tree.') 165 166 if item < node.value: 167 node.left = _delete(item, node.left) 168 # Delete left, rotate right 169 if self.get_height(node.right) - self.get_height(node.left) == 2: 170 if self.get_height(node.right.right) >= self.get_height(node.right.left): # Situation 4 171 node = self.single_rotate_right(node) 172 else: # Situation 3 173 node = self.double_rotate_right(node) 174 175 elif item > node.value: 176 node.right = _delete(item, node.right) 177 # Delete right, rotate left 178 if self.get_height(node.left) - self.get_height(node.right) == 2: 179 if self.get_height(node.left.left) >= self.get_height(node.left.right): # Situation 1 180 node = self.single_rotate_left(node) 181 else: # Situation 3 182 node = self.double_rotate_left(node) 183 184 else: # Node found 185 if node.left and node.right: 186 # Minimum node in right sub-tree has no left sub-node, can be used to make replacement 187 # Find minimum node in right sub-tree 188 min_node = self.find_min(node.right) 189 # Replace current node with min_node 190 node.value = min_node.value 191 # Delete min_node in right sub-tree 192 node.right = _delete(min_node.value, node.right) 193 else: 194 if node.left: 195 node = node.left 196 elif node.right: 197 node = node.right 198 else: 199 node = None 200 # Update node height (if not ratated, update is necessary.) 201 # If node is None, height is -1 already. 202 if node: 203 node.height = max(self.get_height(node.left), self.get_height(node.right)) + 1 204 return node 205 self._root = _delete(item, self._root) 206 207 208 def test(avl): 209 print('\nInit an AVL tree:') 210 for i in [1, 2, 3, 4, 5, 6, 7]: 211 avl.insert(i) 212 avl.show() 213 214 print('\nLeft single rotate:') 215 avl.delete(5) 216 avl.delete(7) 217 avl.delete(6) 218 avl.show() 219 220 avl.make_empty() 221 print('\nInit an AVL tree:') 222 for i in [1, 2, 3, 4, 5, 6, 7]: 223 avl.insert(i) 224 225 print('\nRight single rotate:') 226 avl.delete(1) 227 avl.delete(3) 228 avl.delete(2) 229 avl.show() 230 231 avl.make_empty() 232 print('\nInit an AVL tree:') 233 for i in [6, 2, 8, 1, 4, 9, 3, 5]: 234 avl.insert(i) 235 avl.show() 236 237 print('\nRight-Left double rotate:') 238 avl.delete(9) 239 avl.show() 240 241 avl.make_empty() 242 print('\nInit an AVL tree:') 243 for i in [4, 2, 8, 1, 6, 9, 5, 7]: 244 avl.insert(i) 245 avl.show() 246 247 print('\nLeft-Right double rotate:') 248 avl.delete(1) 249 avl.show() 250 251 if __name__ == '__main__': 252 test(AvlTree())