引用链接
二叉搜索树的效率
平均情况下,二叉搜索树进行搜索的时间复杂度为O(lgn)
最坏情况下,二叉搜索树可能非常偏斜。
解决方案:随机化插入、AVL树
AVL树
AVL树:AVL树是一颗自平衡的二叉搜索树。
AVL树具有以下性质:
根的左右子树的高度之差的绝对值不能超过1
根的左右子树都是平衡二叉树
AVL树–插入
插入一个节点可能会破坏AVL树的平衡,可以通过旋转操作来进行修正。
插入一个节点后,只有从插入节点到根节点的路径上的节点的平衡可能被改变。我们需要找出第一个破坏了平衡条件的节点,称之为K。K的两颗子树的高度差2。
不平衡的出现可能有四种情况:
1. AVL插入–左旋:
不平衡是由于对K的右孩子的右子树插入导致的:左旋
2.AVL插入–右旋:
不平衡是由于对K的左孩子的左子树插入导致的:右旋
3.AVL插入–右旋-左旋:
不平衡是由于对K的右孩子的左子树插入导致的:右旋-左旋
4.AVL插入–左旋-右旋:
不平衡是由于对K的左孩子的右子树插入导致的:左旋-右旋
from BinaryTree_delete 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):
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
if g.bf>0: #右偏,插入到s3上
p.bf=-1
c.bf=0
elif g.bf<0: #左偏,插入到s2上
p.bf=0
c.bf=1
else: #插入的是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
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
#更新bf
if g.bf<0:
p.bf=1
c.bf=0
if g.bf>0:
p.bf=0
c.bf=-1
else:
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: #val==p.data
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.parent.bf<0,有两种情况,右旋,左旋右旋,所以要看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)
#记得:把n和g连起来
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: #node.bf=-1,左边沉
n=self.rotate_right_left(node.parent,node)
else: #node.bf=1,右边沉
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
tree=AVLTree([9,8,7,6,5,4,3,2,1])
tree.pre_order(tree.root)
print("\n")
tree.in_order(tree.root)
二叉搜索树扩展应用–B树
B树(B-Tree):B树是一颗自平衡的多路搜索树。常用于数据库的索引。