zoukankan      html  css  js  c++  java
  • 数据结构树之AVL树(平衡二叉树)

    一 什么是AVL树(平衡二叉树):

    AVL树本质上是一颗二叉查找树,但是它又具有以下特点:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为平衡二叉树。下面是平衡二叉树和非平衡二叉树对比的例图:

    平衡因子(bf):结点的左子树的深度减去右子树的深度,那么显然-1<=bf<=1;

    AVL树具有以下性质:

    • 根的左右子树的高度之差的绝对值不能超过1
    • 根的左右子树都是平衡二叉树

    二 AVL树的旋转

    插入一个节点可能会破坏AVL树的平衡, 可以通过旋转操作来进行修正
    插入一个节点后,只有从插入节点到根节点的路径上的节点的平衡可能被改变。我们需要找出第一个破坏了平衡条件的节点,称之为K。K的两颗子树高度相差2
    不平衡的出可能有4种情况:

    • 不平衡是由于对k的右孩子的右子树插入导致的:左旋
    • 不平衡是由于对k的左孩子的左子树插入导致的:右旋
    • 不平衡是由于对k的右孩子的左子树插入导致的:右旋-左旋
    • 不平衡是由于对k的左孩子的右子树插入导致的:左旋-右旋

    1 左旋

    我们在进行节点插入的时候,可能会出现节点都倾向于左边的情况,例如:

    这个时候,我们就可以对节点9进行右旋操作,使它恢复平衡。

    即:顺时针旋转两个节点,使得父节点被自己的左孩子取代,而自己成为自己的右孩子

     再举个例子:

    节点4和9高度相差大于1。由于是左孩子的高度较高,此时是左-左型,进行右旋。

     2 左旋

    左旋和右旋一样,就是用来解决当大部分节点都偏向右边的时候,通过左旋来还原。例如:

    3 右旋左旋

    对于图中画圈部分

    单单一次左旋或右旋是不行的,下面我们先说说如何处理这种情况。

     处理的方法是先对节点10进行右旋把它变成右-右型。

    然后在进行左旋。

    调整之后:

     4 左旋右旋

    同理,也存在左-右型的,例如:

     

    对于左-右型的情况和刚才的 右-左型相反,我们需要对它进行一次左旋,再右旋。

    在插入的过程中,会出现一下四种情况破坏AVL树的特性,我们可以采取如下相应的旋转。

    1、左-左型:做右旋。

    2、右-右型:做左旋转。

    3、左-右型:先做左旋,后做右旋。

    4、右-左型:先做右旋,再做左旋。

    三 AVL树的实现代码

      1 class AVLNode(object):
      2     def __init__(self, data):
      3         '''
      4         AVL树的每个节点
      5         '''
      6 
      7         self.data = data
      8         self.lchild = None
      9         self.rchild = None
     10         self.parent = None
     11         self.bf = 0
     12 
     13 
     14 class AVLTree(object):
     15     '''
     16     AVL树相关操作
     17     '''
     18 
     19     def __init__(self, li=None):
     20         self.root = None
     21         if li:
     22             for val in li:
     23                 self.insert_no_rec(val)
     24 
     25     def pre_order(self, root):
     26         if root:
     27             print(root.data, end=",")
     28             self.pre_order(root.lchild)
     29             self.pre_order(root.rchild)
     30 
     31     def in_order(self, root):
     32         if root:
     33             self.in_order(root.lchild)
     34             print(root.data, end=',')
     35             self.in_order(root.rchild)
     36 
     37     def rotate_left(self, p, c):
     38         s2 = c.lchild
     39         p.rchild = s2
     40         if s2:
     41             s2.parent = p
     42         c.lchild = p
     43         p.parent = c
     44         p.bf = 0
     45         c.bf = 0
     46         return c
     47 
     48     def rotate_right(self, p, c):
     49         s2 = c.rchild
     50         p.lchild = s2
     51         if s2:
     52             s2.parent = p
     53         c.rchild = p
     54         p.parent = c
     55         p.bf = 0
     56         c.bf = 0
     57         return c
     58 
     59     def rotate_right_left(self, p, c):
     60         g = c.lchild
     61         s3 = g.rchild
     62         c.lchild = s3
     63         if s3:
     64             s3.parent = c
     65         g.rchild = c
     66         c.parent = g
     67 
     68         s2 = g.lchild
     69         p.rchild = s2
     70         if s2:
     71             s2.parent = p
     72         g.lchild = p
     73         p.parent = g
     74 
     75         # 更新bf
     76         if g.bf > 0:
     77             p.bf = -1
     78             c.bf = 0
     79         elif g.bf < 0:
     80             p.bf = 0
     81             c.bf = 1
     82         else:  # 插入的是g
     83             p.bf = 0
     84             c.bf = 0
     85         return g
     86 
     87     def rotate_left_right(self, p, c):
     88         g = c.rchild
     89         s2 = g.lchild
     90         c.rchild = s2
     91         if s2:
     92             s2.parent = c
     93         g.lchild = c
     94         c.parent = g
     95 
     96         s3 = g.rchild
     97         p.lchild = s3
     98         if s3:
     99             s3.parent = p
    100         g.rchild = p
    101         p.parent = g
    102 
    103         # 更新bf
    104         if g.bf < 0:
    105             p.bf = 1
    106             c.bf = 0
    107         elif g.bf > 0:
    108             p.bf = 0
    109             c.bf = -1
    110         else:
    111             p.bf = 0
    112             c.bf = 0
    113         return g
    114 
    115     def insert_no_rec(self, val):
    116         # 1 和BST一样插入
    117         p = self.root
    118         if not p:  # 空树
    119             self.root = AVLNode(val)
    120             return
    121         while True:
    122             if val < p.data:
    123                 if p.lchild:
    124                     p = p.lchild
    125                 else:  # 左子树不存在直接插入
    126                     p.lchild = AVLNode(val)
    127                     p.lchild.parent = p
    128                     node = p.lchild  # node存储就是插入的节点
    129                     break
    130             elif val > p.data:
    131                 if p.rchild:
    132                     p = p.rchild
    133                 else:
    134                     p.rchild = AVLNode(val)
    135                     p.rchild.parent = p
    136                     node = p.rchild
    137                     break
    138             else:  # val == p.data 一颗树如果插入同样的元素 不操作
    139                 return
    140         # 更新balance factor
    141         while node.parent:  # node的parent不空
    142             if node.parent.lchild == node:  # 传递是从左子树来的, 左子树更沉了
    143                 # 更新node.parent的bf -=1
    144                 if node.parent.bf < 0:  # 原来node.parent.bf == -1, 更新后变成-2
    145                     # 看node哪边沉
    146                     g = node.parent.parent  # 为了连接旋转之后的子树
    147                     x = node.parent  # 旋转之前子树的根
    148                     if node.bf > 0:
    149                         n = self.rotate_left_right(node.parent, node)
    150                     else:
    151                         n = self.rotate_right(node.parent, node)
    152                 elif node.parent.bf > 0:  # 原来node.parent.bf=1 更新之后变成0
    153                     node.parent.bf = 0
    154                     break
    155                 else:  # 原来node.parent.bf=0 更新之后变成-1
    156                     node.parent.bf = -1
    157                     node = node.parent
    158                     continue
    159             else:  # 传递是从右子树来的, 右子树更沉了
    160                 # 更新node.parent.bf += 1
    161                 if node.parent.bf > 0:  # 原来node.parent.bf ==1, 更新后变成2
    162                     # 做旋转
    163                     # 看node那边沉
    164                     g = node.parent.parent  # 为了连接旋转之后的子树
    165                     x = node.parent  # 旋转之前子树的根
    166                     if node.bf < 0:  # node.bf = 1
    167                         n = self.rotate_left_right(node.parent, node)
    168                     else:  # node.bf = -1
    169                         n = self.rotate_left(node.parent, node)
    170 
    171                 elif node.parent.bf < 0:  # 原来node.parent.bf = -1 更新后变成0
    172                     node.parent.bf = 0
    173                     break
    174                 else:  # 原来node.parent.bf =0 更新之后变成1
    175                     node.parent.bf = 1
    176                     node = node.parent
    177                     continue
    178             # 链接旋转后的子树
    179             n.parent = g
    180             if g:  # g不是空
    181                 if x == g.lchild:
    182                     g.lchild = n
    183                 else:
    184                     g.rchild = n
    185                 break
    186             else:
    187                 self.root = n
    188                 break
    189 
    190 
    191 tree = AVLTree([9, 8, 7, 6, 5, 4, 3, 2, 1])
    192 tree.pre_order(tree.root)
    193 print("")
    194 tree.in_order(tree.root)
  • 相关阅读:
    Java实现 蓝桥杯VIP 算法训练 数的统计
    Java实现 蓝桥杯VIP 算法训练 和为T
    Java实现 蓝桥杯VIP 算法训练 友好数
    Java实现 蓝桥杯VIP 算法训练 连续正整数的和
    Java实现 蓝桥杯VIP 算法训练 寂寞的数
    Java实现 蓝桥杯VIP 算法训练 学做菜
    Java实现 蓝桥杯VIP 算法训练 暗恋
    Java实现 蓝桥杯VIP 算法训练 暗恋
    测试鼠标是否在窗口内,以及测试鼠标是否在窗口停留
    RichEdit 各个版本介绍
  • 原文地址:https://www.cnblogs.com/harryblog/p/10675177.html
Copyright © 2011-2022 走看看