数据结构与算法【Python实现】(七)平衡二叉搜索树/AVL树

一、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),是一棵自平衡的多路搜索树,常用于数据库的索引

 将查询分成硬盘块访问

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值