一、AVL树概念
AVL树:AVL树是一棵自平衡的二叉搜索树
AVL树具有以下性质:
根的左右子树的高度之差的绝对值不能超过1
根的左右子树都是平衡二叉树
balance factor:记录左右子树高度之差,这里为 右高度-左高度
二、AVL树的旋转
动画:
Binary Search Tree, AVL Tree - VisuAlgo
插入一个节点可能会破坏AVL树的平衡,可以通过旋转操作来修正
插入一个节点后,只有从插入节点到根节点的路径上的节点的平衡可能被改变。我们需要找出第一个被破坏了平衡条件的节点,称之为P。P的两棵子树的高度差为2.
不平衡的出现可能有四种情况:
1、不平衡是由于对P的右孩子的右子树插入导致的:左旋
2、不平衡是由于对P的左孩子的左子树插入导致的:右旋
3、不平衡是由于对P的右孩子的左子树插入导致的:右旋-左旋
4、不平衡是由于对P的左孩子的右子树插入导致的:左旋-右旋
from binary_search_tree import BiTreeNode, BST
class AVLNode(BiTreeNode):
def __init__(self, data):
BiTreeNode.__init__(self,data)
self.bf = 0 #平衡因子
class AVLTree(BST):
def __init__(self,li=None):
BST.__init__(self,li)
#左旋
def rotate_left(self,p,c): #p和c为要左旋的节点
s2 = c.lchild
p.rchild = s2
if s2:
s2.parent = p
c.lchild = p
p.parent = c
#更新平衡因子
p.bf = 0
c.bf = 0
return c
#右旋
def rotate_right(self,p,c):
s2 = c.rchild
p.lchild = s2
if s2:
s2.parent = p
c.rchild = p
p.parent = c
p.bf = 0
c.bf = 0
return c
#右旋-左旋
def rotate_right_left(self,p,c):
#右旋
g = c.lchild
s3 = g.rchild
c.lchild = s3
if s3:
s3.parent = c
g.rchild = c
c.parent = g
#左旋
s2 = g.lchild
p.rchild = s2
if s2:
s2.parent = p
g.lchild = p
p.parent = g
#更新bf bf=右-左
if g.bf > 0: #如果插入之前s2,s3为h-1,插入到s3上
p.bf = -1
c.bf = 0
elif g.bf < 0: #如果插入之前s2,s3为h-1,插入到s2上
p.bf = 0
c.bf = 1
else: #如果s1,s2,s3,s4都是空,插入的实际上是g 此时g.bf=0
p.bf = 0
c.bf = 0
return g
#左旋-右旋
def rotate_left_right(self,p,c):
#左旋
g = c.rchild
s2 = g.lchild
c.rchild = s2
if s2:
s2.parent = c
g.lchild = c
c.parent =g
#右旋
s3 = g.rchild
p.lchild = s3
if s3:
s3.parent = p
g.rchild = p
p.parent = g
#更新平衡因子
if g.bf < 0: #插入到s2
p.bf = 1
c.bf = 0
elif g.bf > 0: #插入到s3
p.bf = 0
c.bf = -1
else: #插入的是g
p.bf = 0
c.bf = 0
return g
三、AVL树的插入
def insert_no_rec(self, val):
#1、和BST一样,先插入
p = self.root
if not p: # 如果p是空 整个树为空树
self.root = AVLNode(val)
return
while True:
if val < p.data:
if p.lchild: # 如果左子树存在
p = p.lchild
else: # 如果左子树不存在 直接插入
p.lchild = AVLNode(val)
p.lchild.parent = p
node = p.lchild #node存储插入的节点
break
elif val > p.data:
if p.rchild:
p = p.rchild
else:
p.rchild = AVLNode(val)
p.rchild.parent = p
node = p.rchild
break
else:
return # 如果等于直接返回
#2、更新balance factor
while node.parent: #node的parent不空
if node.parent.lchild == node: #传递是从左子树来的,左子树更沉了
#更新node的parent的bf -= 1
if node.parent.bf < 0: #原来node.parent.bf = -1,更新后为-2
#看node哪边沉 做旋转
A = node.parent.parent #为了连接旋转后的子树
x = node.parent #旋转前的子树的根
if node.bf > 0: #右边沉 左右 左旋右旋
n = self.rotate_left_right(node.parent,node)
else: #左边沉 左左 右旋
n = self.rotate_right(node.parent,node)
#记得:把n和A连起来
elif node.parent.bf > 0: #原来node.parent.bf = 1,更新后为0
node.parent.bf = 0
#等于零 不再传递
break
else: #原来的node.parent.bf = 0,更新后为-1
node.parent.bf = -1
node = node.parent #传递向上走
continue
else: #传递是从右子树来的,右子树更沉了
# 更新node的parent的bf += 1
if node.parent.bf > 0: #原来node.parent.bf = 1,更新后为2
# 看node哪边沉 做旋转
A = node.parent.parent
x = node.parent # 旋转前的子树的根
if node.bf < 0: #左边沉 右左 右旋左旋
n = self.rotate_right_left(node.parent,node)
else: #右边沉 右右 左旋
n = self.rotate_left(node.parent,node)
# 记得:把n和A连起来
elif node.parent.bf < 0: #原来node.parent.bf = -1,更新后为0
node.parent.bf = 0
# 等于零 不再传递
break
else: #原来的node.parent.bf = 0,更新后为1
node.parent.bf = 1
node = node.parent
continue
#旋转后n和A连接
n.parent = A
if A: #判断g是否存在 如果g不空
if x == A.lchild: #原来g的左孩子是node.parent
A.lchild = n
else:
A.rchild = n
break
else: #如果g空 则n是根节点
self.root = n
break
<每一次插入都维持AVL树的平衡>
四、AVL树扩展应用——B树
B树(B-Tree),是一棵自平衡的多路搜索树,常用于数据库的索引
将查询分成硬盘块访问