一 什么是AVL树(平衡二叉树):
AVL树本质上是一颗二叉查找树,但是它又具有以下特点:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为平衡二叉树。下面是平衡二叉树和非平衡二叉树对比的例图:
平衡因子(bf):结点的左子树的深度减去右子树的深度,那么显然-1<=bf<=1;
AVL树具有以下性质:
- 根的左右子树的高度之差的绝对值不能超过1
- 根的左右子树都是平衡二叉树
二 AVL树的旋转
插入一个节点可能会破坏AVL树的平衡, 可以通过旋转操作来进行修正
插入一个节点后,只有从插入节点到根节点的路径上的节点的平衡可能被改变。我们需要找出第一个破坏了平衡条件的节点,称之为K。K的两颗子树高度相差2
不平衡的出可能有4种情况:
- 不平衡是由于对k的右孩子的右子树插入导致的:左旋
- 不平衡是由于对k的左孩子的左子树插入导致的:右旋
- 不平衡是由于对k的右孩子的左子树插入导致的:右旋-左旋
- 不平衡是由于对k的左孩子的右子树插入导致的:左旋-右旋
1 左旋
我们在进行节点插入的时候,可能会出现节点都倾向于左边的情况,例如:
这个时候,我们就可以对节点9进行右旋操作,使它恢复平衡。
即:顺时针旋转两个节点,使得父节点被自己的左孩子取代,而自己成为自己的右孩子
再举个例子:
节点4和9高度相差大于1。由于是左孩子的高度较高,此时是左-左型,进行右旋。
2 左旋
左旋和右旋一样,就是用来解决当大部分节点都偏向右边的时候,通过左旋来还原。例如:
3 右旋左旋
对于图中画圈部分
单单一次左旋或右旋是不行的,下面我们先说说如何处理这种情况。
处理的方法是先对节点10进行右旋把它变成右-右型。
然后在进行左旋。
调整之后:
4 左旋右旋
同理,也存在左-右型的,例如:
对于左-右型的情况和刚才的 右-左型相反,我们需要对它进行一次左旋,再右旋。
在插入的过程中,会出现一下四种情况破坏AVL树的特性,我们可以采取如下相应的旋转。
1、左-左型:做右旋。
2、右-右型:做左旋转。
3、左-右型:先做左旋,后做右旋。
4、右-左型:先做右旋,再做左旋。
三 AVL树的实现代码
1 class AVLNode(object): 2 def __init__(self, data): 3 ''' 4 AVL树的每个节点 5 ''' 6 7 self.data = data 8 self.lchild = None 9 self.rchild = None 10 self.parent = None 11 self.bf = 0 12 13 14 class AVLTree(object): 15 ''' 16 AVL树相关操作 17 ''' 18 19 def __init__(self, li=None): 20 self.root = None 21 if li: 22 for val in li: 23 self.insert_no_rec(val) 24 25 def pre_order(self, root): 26 if root: 27 print(root.data, end=",") 28 self.pre_order(root.lchild) 29 self.pre_order(root.rchild) 30 31 def in_order(self, root): 32 if root: 33 self.in_order(root.lchild) 34 print(root.data, end=',') 35 self.in_order(root.rchild) 36 37 def rotate_left(self, p, c): 38 s2 = c.lchild 39 p.rchild = s2 40 if s2: 41 s2.parent = p 42 c.lchild = p 43 p.parent = c 44 p.bf = 0 45 c.bf = 0 46 return c 47 48 def rotate_right(self, p, c): 49 s2 = c.rchild 50 p.lchild = s2 51 if s2: 52 s2.parent = p 53 c.rchild = p 54 p.parent = c 55 p.bf = 0 56 c.bf = 0 57 return c 58 59 def rotate_right_left(self, p, c): 60 g = c.lchild 61 s3 = g.rchild 62 c.lchild = s3 63 if s3: 64 s3.parent = c 65 g.rchild = c 66 c.parent = g 67 68 s2 = g.lchild 69 p.rchild = s2 70 if s2: 71 s2.parent = p 72 g.lchild = p 73 p.parent = g 74 75 # 更新bf 76 if g.bf > 0: 77 p.bf = -1 78 c.bf = 0 79 elif g.bf < 0: 80 p.bf = 0 81 c.bf = 1 82 else: # 插入的是g 83 p.bf = 0 84 c.bf = 0 85 return g 86 87 def rotate_left_right(self, p, c): 88 g = c.rchild 89 s2 = g.lchild 90 c.rchild = s2 91 if s2: 92 s2.parent = c 93 g.lchild = c 94 c.parent = g 95 96 s3 = g.rchild 97 p.lchild = s3 98 if s3: 99 s3.parent = p 100 g.rchild = p 101 p.parent = g 102 103 # 更新bf 104 if g.bf < 0: 105 p.bf = 1 106 c.bf = 0 107 elif g.bf > 0: 108 p.bf = 0 109 c.bf = -1 110 else: 111 p.bf = 0 112 c.bf = 0 113 return g 114 115 def insert_no_rec(self, val): 116 # 1 和BST一样插入 117 p = self.root 118 if not p: # 空树 119 self.root = AVLNode(val) 120 return 121 while True: 122 if val < p.data: 123 if p.lchild: 124 p = p.lchild 125 else: # 左子树不存在直接插入 126 p.lchild = AVLNode(val) 127 p.lchild.parent = p 128 node = p.lchild # node存储就是插入的节点 129 break 130 elif val > p.data: 131 if p.rchild: 132 p = p.rchild 133 else: 134 p.rchild = AVLNode(val) 135 p.rchild.parent = p 136 node = p.rchild 137 break 138 else: # val == p.data 一颗树如果插入同样的元素 不操作 139 return 140 # 更新balance factor 141 while node.parent: # node的parent不空 142 if node.parent.lchild == node: # 传递是从左子树来的, 左子树更沉了 143 # 更新node.parent的bf -=1 144 if node.parent.bf < 0: # 原来node.parent.bf == -1, 更新后变成-2 145 # 看node哪边沉 146 g = node.parent.parent # 为了连接旋转之后的子树 147 x = node.parent # 旋转之前子树的根 148 if node.bf > 0: 149 n = self.rotate_left_right(node.parent, node) 150 else: 151 n = self.rotate_right(node.parent, node) 152 elif node.parent.bf > 0: # 原来node.parent.bf=1 更新之后变成0 153 node.parent.bf = 0 154 break 155 else: # 原来node.parent.bf=0 更新之后变成-1 156 node.parent.bf = -1 157 node = node.parent 158 continue 159 else: # 传递是从右子树来的, 右子树更沉了 160 # 更新node.parent.bf += 1 161 if node.parent.bf > 0: # 原来node.parent.bf ==1, 更新后变成2 162 # 做旋转 163 # 看node那边沉 164 g = node.parent.parent # 为了连接旋转之后的子树 165 x = node.parent # 旋转之前子树的根 166 if node.bf < 0: # node.bf = 1 167 n = self.rotate_left_right(node.parent, node) 168 else: # node.bf = -1 169 n = self.rotate_left(node.parent, node) 170 171 elif node.parent.bf < 0: # 原来node.parent.bf = -1 更新后变成0 172 node.parent.bf = 0 173 break 174 else: # 原来node.parent.bf =0 更新之后变成1 175 node.parent.bf = 1 176 node = node.parent 177 continue 178 # 链接旋转后的子树 179 n.parent = g 180 if g: # g不是空 181 if x == g.lchild: 182 g.lchild = n 183 else: 184 g.rchild = n 185 break 186 else: 187 self.root = n 188 break 189 190 191 tree = AVLTree([9, 8, 7, 6, 5, 4, 3, 2, 1]) 192 tree.pre_order(tree.root) 193 print("") 194 tree.in_order(tree.root)