[数据结构] 平衡二叉树的实现

1 二叉排序树

对一个二叉树中序遍历能得到一个有序序列的二叉树即为二叉排序树

2 平衡因子

二叉树上的节点左右子树的深度之差为平衡因子,记为BF(Blance Factor)

3 平衡二叉树

一个二叉排序树中各节点的左右子树高度差不超过1,也即每个节点的平很因子BF为0、-1或1

4 最小不平衡子树

插入一个节点,往上寻找“祖辈节点”,最近的BF绝对值大于1 的节点为根的子树为最小不平衡子树

5 平衡二叉树实现原理

当插入节点导致平衡二叉树的平衡性被破坏(存在|BF|>1的节点),保持二叉排序树特性,通过对最小不平衡子树进行左旋或右旋,使之称为新的平衡子树从而整个二叉树重归平衡

6 旋转

6.1 右旋(LL型)

  原因:由于对p的左孩子的左子树插入,导致平衡性被破坏
  解决:在最小不平衡子树中,根结点变为左孩子的右孩子,新根结点原来的的右孩子(如果有)给旧的的根节点当左孩子

  LL型两种基本最小不平衡子树及其旋转过程:
    (1)该最小不平衡子树的根结点为6,新插入结点为1
在这里插入图片描述
    (2)该最小不平衡子树的根结点为6,新插入结点为2
在这里插入图片描述

6.2 左旋(RR型)

  原因:由于对p的右孩子的右子树插入,导致平衡性被破坏
  解决:在最小不平衡子树中,根节点变为右孩子的左孩子,新根节点原来的的左孩子(如果有)给旧的的根节点当右孩子

  RR型两种基本最小不平衡子树及其旋转过程:
    (1)该最小不平衡子树的根结点为6,新插入结点为8
在这里插入图片描述
    (2)该最小不平衡子树的根结点为2,新插入结点为6
在这里插入图片描述

6.3 LR 型

  原因:由于对p的左孩子的右子树插入,导致平衡性被破坏
  解决:根节点的左孩子先左旋,然后再对调整后的根节点右旋。

  LR型两种基本最小不平衡子树及其旋转过程:
    (1)该最小不平衡子树的根结点为6,新插入结点为5
在这里插入图片描述
    (2)该最小不平衡子树的根结点为6,新插入结点为5
在这里插入图片描述

6.4 RL型

  原因:由于对p的右孩子的左子树插入,导致平衡性被破坏
  解决:根节点的右孩子先右旋,然后再对调整后的根节点左旋。

  RL型两种基本最小不平衡子树及其旋转过程:
    (1)该最小不平衡子树的根结点为6,新插入结点为5
在这里插入图片描述
    (2)该最小不平衡子树的根结点为4,新插入结点为5
在这里插入图片描述

7 代码

7.1 关于旋转后bf值的更新

假设:
  - 最小不平衡子树中,受插入结点影响bf值改变的祖辈结点有3个或2个,算上插入结点,分别记为(1)node、g、c、f和(2)g(也即node插作g)、c、f。

观察可得:
  - LL型和RR型的p结点和c结点的bf值最终变都变为0,其他结点的bf值自更新后(插入结点后向上更新bf值,找最小不平衡子树)便不变。
  - LR型和RL型中:
    - 若为(2)类
      - 则p.bf - > 0, c.bf -> 0
    - 若为(1)类,
      - LR型中新结点插作g的左孩子则g.bf -> 0, c.bf -> 0, p.b f-> -1
      - LR型中新结点插作g的右孩子则g.bf -> 0, c.bf -> 1, p.b f-> 0
      - RL型中新结点插作g的左孩子则g.bf -> 0, c.bf -> -1, p.bf -> 0
      - RL型中新结点插作g的右孩子则g.bf -> 0, c.bf -> 0, p.bf -> 1

7.1 AVLT代码

class AVLT:
    class Node:
        def __init__(self,data):
            self.data = data
            self.lchild = None
            self.rchild = None
            self.parent = None
            self.bf = 0
    
    def __init__(self, _list=[]):
        self.root = None
        if _list: # 根据提供的列表中结点值来初始化树
            for v in _list:
                self.insert_no_rec(v) 

7.2 旋转代码

    def LLSpin(self, p, c):
        """LL型:
        根结点变作左孩子的右孩子,新根结点原来的的右孩子(如果有)给旧的的根结点当左孩子;
        c 为最小不平衡子树根节点的左孩子, p为最小不平衡子树的根节点"""
        
        c.parent = p.parent #新根结点c继承旧根结点p的父亲
        if p.parent:# 先判断,旧根结点不是整树的根结点
            # 再判断旧根结点是其原父亲结点的左孩子还是右孩子,让新根结点继承
            if p.parent.lchild == p:
                p.parent.lchild = c
            elif p.parent.rchild == p:
                p.parent.rchild = c
        else: # 如果旧根结点p为整树的根,则要更新一下self.root
            self.root = c    
            
        p.lchild = c.rchild # p的lchild不再是c了,c如果有右孩子,p就指向该右孩子,没有就指向None
        if c.rchild: # c如果有右孩子,给p当左孩子
            c.rchild.parent = p
        # p得到c的右孩子后再给c当右孩子
        c.rchild = p
        p.parent = c
        
        # 更新bf值
        p.bf = 0
        c.bf = 0
        
        return c
        
       
    def RRSpin(self, p, c):
        """RR型:
        	根节点变为右孩子的左孩子,新根节点原来的的左孩子(如果有)给旧的的根节点当右孩子
        	c 为最小不平衡子树根节点的右孩子, p为最小不平衡子树的根节点"""
        
        c.parent = p.parent #新根结点c继承旧根结点p的父亲
        if p.parent:# 先判断,旧根结点不是整树的根结点
            # 再判断旧根结点是其原父亲结点的左孩子还是右孩子,让新根结点继承
            if p.parent.lchild == p:
                p.parent.lchild = c
            elif p.parent.rchild == p:
                p.parent.rchild = c
        else: # 如果旧根结点p为整树的根则,要更新一下self.root
            self.root = c
        p.rchild = c.lchild # p的rchild再是c了,c如果有左孩子,p就指向该左孩子,没有就指向None
        if c.lchild: # c如果有左孩子,给p当右孩子
            c.lchild.parent = p
            p.bf = 0
            c.bf = 0
        # p得到c的左孩子后再给c当左孩子
        c.lchild = p
        p.parent = c
        
        # 更新bf值
        p.bf = 0
        c.bf = 0
        
        return c

        
    def LRSpin(self, p, c):
    """LR型:根节点的左孩子先左旋,然后再对调整后的根节点右旋"""
        g = c.rchild
        self.RRSpin(c,g)
        self.LLSpin(p,g)
        
        # 更新bf
        if g.bf > 0: # 插入作g左孩子
            # 旋转后
            p.bf = -1 
            c.bf = 0
            g.bf = 0
        elif g.bf < 0: # 插入作g右孩子
            # 旋转后
            p.bf = 0 
            c.bf = 1
            g.bf = 0
        elif g.bf == 0: # 插入作为g
            p.bf = 0
            c.bf = 0
            
        return g

    def RLSpin(self, p, c):
    """RL型:根节点的右孩子先右旋,然后再对调整后的根节点左旋"""
        g = c.lchild
        self.LLSpin(c,g)
        self.RRSpin(p,g)
        
        # 更新bf
        if g.bf > 0:# 插入作g左孩子
            # 旋转后
            p.bf = 0 
            c.bf = -1
            g.bf = 0
        elif g.bf < 0:# 插入作g右孩子
            # 旋转后
            p.bf = 1 
            c.bf = 0
            g.bf = 0
        elif g.bf == 0:# 插入作为g
            p.bf = 0
            c.bf = 0
            
        return g

7.3 插入代码

    def insert_no_rec(self, val):
        """ 1. 若插作根结点,直接赋值并返回,且不必维护bf
            2. 若插作非根结点,根据二叉排序树特性,插入
            3. 插入结点后,若要维护bf,则从插入结点往上维护他的父亲或父亲的父亲,维护到新根结点
            4. 关于维护bf,当维护到新根结点时,不必再往上维护,只需调用旋转函数,该函数内部会再对整个不平衡子树的关键结点的bf进行维护
        """
        par = self.root
        if not par:
            self.root = AVLT.Node(val)
            return 
        while True:
            if val < par.data:
                if par.lchild:
                    par = par.lchild
                else:
                    par.lchild = AVLT.Node(val)
                    par.lchild.parent = par
                    node = par.lchild # node即为插入的结点
                    break
            elif val > par.data:
                if par.rchild:
                    par = par.rchild
                else:
                    par.rchild = AVLT.Node(val)
                    par.rchild.parent = par
                    node = par.rchild
                    break
            elif val == par.data:
                return # -*-本例中默认不重复关键字(结点的值)-*-
            
        # 2.寻找最小不平衡子树并做旋转
        """思想:从新插入的结点向上更新他的祖辈结点的bf值,直到发现bf绝对值为2或没有发现bf绝对值为2的祖辈结点"""
        while node.parent: # 向上更新
            """解决最小不平衡子树的旋转类型受新结点插入在左子树还是右子树的影响"""
            if node.parent.lchild == node:
                if node.parent.bf == 0: # 1.刚插入时 2.插入后第二次循环,node指向g结点
                    node.parent.bf = 1 # 插入导致左比右深1
                    node = node.parent # node指向父节点
                elif node.parent.bf == -1: # 插入后父亲结点bf平衡,不会影响整体平衡性
                    node.parent.bf = 0
                    break
                elif node.parent.bf == 1: # node指向c结点
                    if node.bf == 1: # LL型
                        p = node.parent
                        c = node
                        n = self.LLSpin(p, c) # node.parent 为结点p, node为结点c
                    elif node.bf == -1: # LR型
                        p = node.parent
                        c = node
                        n = self.LRSpin(p, c)
            elif node.parent.rchild == node:
                if node.parent.bf == 0: # 1.刚插入时 2.插入后第二次循环,node指向g结点
                    node.parent.bf = -1 # 插入导致右比左深1
                    node = node.parent # node指向父节点
                elif node.parent.bf == 1: # 插入后父亲结点bf平衡,不会影响整体平衡性,无须继续往上走
                    node.parent.bf = 0
                    break 
                elif node.parent.bf == -1: #node指向c结点
                    if node.bf == -1: # RR型
                        p = node.parent
                        c = node
                        n = self.RRSpin(p.data, c.data)
                    elif node.bf == 1: # RL型
                        p = node.parent
                        c = node
                        n = self.RLSpin(p, c)

8 前序、中序、后序、层序遍历实现

 def pre_order(self, root):
 	"""前序遍历"""
        order_list = []
        def order(root):
            if root:
                order_list.append(root.data)
                order(root.lchild)
                order(root.rchild)
        order(root)
        return order_list
        
    def in_order(self, root):
    	"""中序遍历"""
        order_list = []
        def order(root):
            if root:
                order(root.lchild)
                order_list.append(root.data)
                order(root.rchild)
        order(root)
        return order_list
    
    def post_order(self, root):
    	"""后序遍历"""
        order_list = []
        def order(root):
            if root:
                order(root.lchild)
                order(root.rchild)
                order_list.append(root.data)
        order(root)
        return order_list
    
    def level_order(self, root):
    	"""层序遍历"""
    
        from collections import deque
        order_list = []
        def order(root): 
            dq = deque()
            dq.append(root)
            while len(dq) > 0:
                node = dq.popleft()
                order_list.append(node.data)
                if node.lchild:
                    dq.append(node.lchild)
                if node.rchild:
                    dq.append(node.rchild)
        order(root) 
        return order_list

9 参考

  1. 清华计算机博士带你学习Python算法+数据结构
  2. 《大话数据结构》
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值