AVL树/平衡二叉树(AVL是三个人名的缩写)
- AVL树是一棵自平衡的二叉搜索树
- AVL树具有以下性质:
- 根的左右子树的高度之差的绝对值不能查过1
- 根的左右子树都是平衡二叉树
- balance factor:左右子树的高度之差
AVL树的插入
- 插入一个节点可能会破坏AVL树的平衡,可以通过旋转操作来进行修正
- 插入一个节点后,只有从插入节点到根节点的路径上的节点的平衡可能被改变,需要找出第一个被破坏了平衡条件的节点,称之为K,K的两棵子树的高度差2
- 不平衡的出现可能有4种情况:
- 不平衡是由于对K的右孩子的右子树插入导致的:左旋
- 不平衡是由于对K的左孩子的左子树插入导致的:右旋
- 不平衡是由于对K的右孩子的左子树插入导致的:右旋-左旋
- 不平衡是由于对K的左孩子的右子树插入导致的:左旋-右旋
旋转实现
代码:
from 数据结构-5(树和二叉树) import BiTreeNode, BST
class AVLNode(BiTreeNode):
def __init__(self, data):
BiTreeNode.__init__(self, data)
self.bf = 0 #balance factor
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
#更新bf
p.bf = 0
c.bf = 0
return c
def rotate_right(self, p, c): #右旋
#p和c的位置关系看图
#这个旋转函数在插入的时候用可以,删除的时候不行
s2 = c.rchild
p.lchild = s2
if s2:
s2.parent = p
c.rchild = p
p.parent = c
#更新bf
p.bf = 0
c.bf = 0
return c
def rotate_right_left(self, p, c): #右旋-左旋
g = c.lchild
s2 = g.lchild
s3 = g.rchild
c.lchild = s3
if s3:
s3.parent = c
g.rchild = c
c.parent = g
p.rchild = s2
if s2:
s2.parent = p
g.lchild = p
p.parent = g
#更新bf
if g.bf > 0: #这里bf的计算方式是左减右,此时的情况是s2为h,s3为h-1
p.bf = 0
c.bf = -1
elif g.bf < 0: #此时的情况是s3为h,s2为h-1
p.bf = 1
c.bf = 0
else: #s1s2s3s4都为None,插入的是g
p.bf = 0
c.bf = 0
g.bf = 0
return g
def rotate_left_right(self, p, c): #左旋-右旋
g = c.rchild
s2 = g.lchild
s3 = g.rchild
c.rchild = s2
if s2:
s2.parent = c
g.lchild = c
c.parent = g
p.lchild = s3
if s3:
s3.parent = p
g.rchild = p
p.parent = g
#更新bf
if g.bf > 0: #这里bf的计算方式是左减右,此时的情况是s2为h,s3为h-1
p.bf = -1
c.bf = 0
elif g.bf < 0: #此时的情况是s3为h,s2为h-1
p.bf = 0
c.bf = 1
else: #s1s2s3s4都为None,插入的是g
p.bf = 0
c.bf = 0
g.bf = 0
return g
def insert_no_rec(self, val):
# 1. 和BST一样,插入
p = self.root
if not p: #空树
self.root = BiTreeNode(val)
return
while True:
if val < p.data:
if p.lchild: #左孩子存在
p = p.lchild
else:
p.lchild = BiTreeNode(val)
p.lchild.parent = p
node = p.lchild #node存储的是插入的节点
break
elif val > p.data:
if p.rchild:
p = p.rchild
else:
p.rchild = BiTreeNode(val)
p.rchild.parent = p
node = p.rchild
break
else:
return
# 2. 更新balance factor
while node.parent: # node.parent不空
#这里的node是从下往上遍历的所有节点,不只是刚插入的那个节点了)
if node.parent.lchild == node: #传递是从左子树来的,左子树更沉了
#更新node.parent的bf += 1
if node.parent.bf > 0: #原来node.parent.bf = 1,更新后变成2
#做旋转
#看node哪边沉
g = 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)
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哪边沉
g = 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)
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.parent = g
if g: # g不是空
if x == g.lchild:
g.lchild = n
else:
g.rchild = n
break
else:
self.root = n
break