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(' Init an AVL tree:') 210 for i in [1, 2, 3, 4, 5, 6, 7]: 211 avl.insert(i) 212 avl.show() 213 214 print(' Left single rotate:') 215 avl.delete(5) 216 avl.delete(7) 217 avl.delete(6) 218 avl.show() 219 220 avl.make_empty() 221 print(' Init an AVL tree:') 222 for i in [1, 2, 3, 4, 5, 6, 7]: 223 avl.insert(i) 224 225 print(' Right single rotate:') 226 avl.delete(1) 227 avl.delete(3) 228 avl.delete(2) 229 avl.show() 230 231 avl.make_empty() 232 print(' Init 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(' Right-Left double rotate:') 238 avl.delete(9) 239 avl.show() 240 241 avl.make_empty() 242 print(' Init 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(' Left-Right double rotate:') 248 avl.delete(1) 249 avl.show() 250 251 if __name__ == '__main__': 252 test(AvlTree())
分段解释
首先,需要导入查找树,以及定义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)
接着构建一棵AVL树的类,构造方法可继承查找树,
1 class AvlTree(SearchTree): 2 """ 3 AVL Tree: 4 0 5 _____|_____ 6 | | 7 0 0 8 |___ ___|___ 9 | | | 10 0 0 0 11 |__ 12 | 13 0 14 Insert unbalance: 15 1: | 2: | 3: | 4: 16 A(2) | A(2) | A(2) | A(2) 17 __| | __| | |__ | |__ 18 | | | | | | | 19 B(1) | B(1) | B(1) | B(1) 20 __| | |__ | __| | |__ 21 | | | | | | | 22 ---- | ---- | ---- | ---- 23 |X(0)| | |X(0)| | |X(0)| | |X(0)| 24 ---- | ---- | ---- | ---- 25 26 Delete unbalance: 27 1: | 2: | 3: | 4: 28 A(2) | A(2) | A(2) | A(2) 29 __|__ | __|__ | __|__ | __|__ 30 | | | | | | | | | | | 31 B(1) ---- | B(1) ---- | ---- B(1) | ---- B(1) 32 __| |X(0)| | |__ |X(0)| | |X(0)| __| | |X(0)| |__ 33 | ---- | | ---- | ---- | | ---- | 34 C(0) | C(0) | C(0) | C(0) 35 """
定义获取节点子树高度的方法,当节点为None时,返回高度为-1,
1 def get_height(self, node): 2 if isinstance(node, AvlNode): 3 return node.height 4 return -1
定义向左的单旋转方法,旋转完成后更新高度
1 def single_rotate_left(self, k2): 2 """ 3 K2(2) K1(1) 4 __|__ ____|_____ 5 | | | | 6 K1(1) None(-1) --------> X(0) K2(1) 7 __|__ __|__ 8 | | | | 9 X(0) Y(0) Y(0) None(-1) 10 """ 11 # Left rotate 12 k1 = k2.left 13 k2.left = k1.right 14 k1.right = k2 15 # Update height 16 k2.height = max(self.get_height(k2.left), self.get_height(k2.right)) + 1 17 k1.height = max(self.get_height(k1.left), k2.height) + 1 18 return k1
定义向右的单旋转方法,旋转完成后更新高度
1 def single_rotate_right(self, k2): 2 """ 3 K2(2) K1(2) 4 __|__ ____|_____ 5 | | | | 6 (-1)None K1(1) --------> K2(1) Y(0) 7 __|__ __|__ 8 | | | | 9 X(0) Y(0) (-1)None X(0) 10 """ 11 # Right rotate 12 k1 = k2.right 13 k2.right = k1.left 14 k1.left = k2 15 # Update height 16 k2.height = max(self.get_height(k2.left), self.get_height(k2.right)) + 1 17 k1.height = max(k2.height, self.get_height(k1.right)) + 1 18 return k1
定义先右后左的双旋转方法,旋转完成后更新高度
1 def double_rotate_left(self, k3): 2 """ 3 K3(3) K3(3) K2(2) 4 __|__ __|__ _____|_____ 5 | | right | | left | | 6 K1(2) D(0) --------> K2(2) D(0) --------> K1(1) K3(1) 7 __|__ __|__ __|__ __|__ 8 | | | | | | | | 9 A(0) K2(1) K1(1) C(0) A(0) B(0) C(0) D(0) 10 __|__ __|__ 11 | | | | 12 B(0) C(0) A(0) B(0) 13 """ 14 # Rotate between k1 and k2 15 k3.left = self.single_rotate_right(k3.left) 16 # Rotate between k3 and k2 17 return self.single_rotate_left(k3)
定义先左后右的双旋转方法,旋转完成后更新高度
1 def double_rotate_right(self, k3): 2 """ 3 K3(3) K3(3) K2(2) 4 __|__ __|__ _____|_____ 5 | | left | | right | | 6 D(0) K1(2) --------> D(0) K2(2) --------> K3(1) K1(1) 7 __|__ __|__ __|__ __|__ 8 | | | | | | | | 9 K2(1) A(0) C(0) K1(1) D(0) C(0) B(0) A(0) 10 __|__ __|__ 11 | | | | 12 C(0) B(0) B(0) A(0) 13 """ 14 # Rotate between k1 and k2 15 k3.right = self.single_rotate_left(k3.right) 16 # Rotate between k3 and k2 17 return self.single_rotate_right(k3)
定义插入方法,进行递归插入,每次插入后都需要检测子树高度来决定是否需要对树进行平衡旋转,最后进行高度更新,若经过旋转则此时高度已经被更新。
1 def insert(self, item): 2 if self._root is None: 3 self._root = AvlNode(item) 4 return 5 6 def _insert(item, node): 7 if not node: 8 return AvlNode(item) 9 if item < node.value: 10 node.left = _insert(item, node.left) 11 if self.get_height(node.left) - self.get_height(node.right) == 2: 12 if item < node.left.value: # Situation 1: left --> left 13 node = self.single_rotate_left(node) 14 else: # Situation 2: left --> right 15 node = self.double_rotate_left(node) 16 elif item > node.value: 17 node.right = _insert(item, node.right) 18 if self.get_height(node.right) - self.get_height(node.left) == 2: 19 if item < node.right.value: # Situation 3: right --> left 20 node = self.double_rotate_right(node) 21 else: # Situation 4: right --> right 22 node = self.single_rotate_right(node) 23 else: pass 24 # Update node height (if not rotated, update is necessary.) 25 node.height = max(self.get_height(node.left), self.get_height(node.right)) + 1 26 return node 27 self._root = _insert(item, self._root)
定义删除方法,进行递归删除,删除后检测是否需要平衡旋转,并选择旋转的方式,删除完成根据需要进行高度更新。
1 def delete(self, item): 2 if self._root is None: 3 return 4 5 def _delete(item, node): 6 if not node: # Node no found 7 # return None 8 raise Exception('Element not in tree.') 9 10 if item < node.value: 11 node.left = _delete(item, node.left) 12 # Delete left, rotate right 13 if self.get_height(node.right) - self.get_height(node.left) == 2: 14 if self.get_height(node.right.right) >= self.get_height(node.right.left): # Situation 4 15 node = self.single_rotate_right(node) 16 else: # Situation 3 17 node = self.double_rotate_right(node) 18 19 elif item > node.value: 20 node.right = _delete(item, node.right) 21 # Delete right, rotate left 22 if self.get_height(node.left) - self.get_height(node.right) == 2: 23 if self.get_height(node.left.left) >= self.get_height(node.left.right): # Situation 1 24 node = self.single_rotate_left(node) 25 else: # Situation 3 26 node = self.double_rotate_left(node) 27 28 else: # Node found 29 if node.left and node.right: 30 # Minimum node in right sub-tree has no left sub-node, can be used to make replacement 31 # Find minimum node in right sub-tree 32 min_node = self.find_min(node.right) 33 # Replace current node with min_node 34 node.value = min_node.value 35 # Delete min_node in right sub-tree 36 node.right = _delete(min_node.value, node.right) 37 else: 38 if node.left: 39 node = node.left 40 elif node.right: 41 node = node.right 42 else: 43 node = None 44 # Update node height (if not ratated, update is necessary.) 45 # If node is None, height is -1 already. 46 if node: 47 node.height = max(self.get_height(node.left), self.get_height(node.right)) + 1 48 return node 49 self._root = _delete(item, self._root)
最后定义一个测试函数,用于测试AVL树的功能。
首先初始化一棵树
1 def test(avl): 2 print(' Init an AVL tree:') 3 for i in [1, 2, 3, 4, 5, 6, 7]: 4 avl.insert(i) 5 avl.show()
得到结果
Init an AVL tree: 4 | 4 2 | __|__ 1 | | | 3 | 2 6 6 | _|_ _|_ 5 | | | | | 7 | 1 3 5 7
接着尝试删除5, 6, 7三个节点,完成一个向左单旋转,
1 print(' Left single rotate:') 2 avl.delete(5) 3 avl.delete(7) 4 avl.delete(6) 5 avl.show()
得到结果,可以看到,此时AVL树已经完成了一个向左的平衡旋转
Left single rotate: 2 | 2 1 | __|__ 4 | | | 3 | 1 4 | _| | | | 3
清空树并再次初始化树后
尝试删除1, 2, 3三个节点,完成一个向右单旋转,
1 print(' Delete element from tree:') 2 avl.delete(1) 3 avl.delete(3) 4 avl.delete(2) 5 avl.show()
得到结果,可以看到,此时AVL树已经完成了一个向右的平衡旋转
Delete element from tree: 6 | 6 4 | __|__ 5 | | | 7 | 4 7 | |_ | | | 5
再次清空树,并建立一棵新的树
1 avl.make_empty() 2 print(' Init an AVL tree:') 3 for i in [6, 2, 8, 1, 4, 9, 3, 5]: 4 avl.insert(i) 5 avl.show()
得到结果
Init an AVL tree: 6 | 6 2 | __|__ 1 | | | 4 | 2 8 3 | _|_ |_ 5 | | | | 8 | 1 4 9 9 | _|_ | | | | 3 5
此时尝试删除节点9,完成一个右左双旋转
1 print(' Right-Left double rotate:') 2 avl.delete(9) 3 avl.show()
得到结果
Right-Left double rotate: 4 | 4 2 | __|__ 1 | | | 3 | 2 6 6 | _|_ _|_ 5 | | | | | 8 | 1 3 5 8
最后再次清空树建立一棵新树,
1 avl.make_empty() 2 print(' Init an AVL tree:') 3 for i in [4, 2, 8, 1, 6, 9, 5, 7]: 4 avl.insert(i) 5 avl.show()
显示结果如下
Init an AVL tree: 4 | 4 2 | __|__ 1 | | | 8 | 2 8 6 | _| _|_ 5 | | | | 7 | 1 6 9 9 | _|_ | | | | 5 7
此时尝试删除节点1,造成不平衡完成一个左右双旋转
1 print(' Left-Right double rotate:') 2 avl.delete(1) 3 avl.show()
得到结果
Left-Right double rotate: 6 | 6 4 | __|__ 2 | | | 5 | 4 8 8 | _|_ _|_ 7 | | | | | 9 | 2 5 7 9
相关阅读
1. 二叉树
2. 查找树