数据结构——AVL和平衡树【自平衡维护】

一、什么是AVL树及AVL树解决了什么问题?

二分搜索树的不足:如果我们以此添加1、2、3、4、5元素构建一个二分搜索树,那么最终会退化成一个链表。

AVL是最早的可以自平衡的二分搜索树结构,平衡二叉树即:对于任意一个节点,左子树和右子树的高度差不能超过1。

                    平衡二叉树的高度和节点数量之间的关系也是O(logn)

将二分搜索树变为平衡二叉树:在二分搜索树的基础上添加   ①标注节点的高度             ②计算平衡因子【左子树高度-右子树高度】 一个树中只要有一处平衡因子的值 >= 2或 <= -2则该树不是平衡二叉树

获得节点高度的方法: node.height = 1 + Math.max(getHeight(node.left),getHeight(node.right));

计算平衡因子的方法:getHeight(node.left) - getHeight(node.right);

AVL树是从二分搜索树升级过来的。所以它满足二分搜索树的基本特征【左子树所有节点都小于其根节点,右子树所有节点都大于其根节点】,AVL树是对于二分搜索树退化成链表的一种解决方案。

二、AVL树如何实现自平衡

加入节点后,打破了平衡性,导致新增节点的祖辈节点深度/平衡因子可能发生变化,于是可以沿着节点向上维护平衡性,由于我们可以使用递归实现,所以沿着节点向上维护平衡性也是一件很简单的事情。

1、自平衡维护——LL右旋转

情况① 插入元素在最终形成不平衡节点的左侧的左侧(LL)——右旋转

在插入节点2之前,该树为一棵平衡二叉树,且满足二分搜索树性质,在插入节点2后发现,节点8的平衡因子变为了2,打破了平衡树的基本条件,同理我们在0、1、2顺序插入构建二叉树的同时也会造成树退化成链表,导致左图中节点12平衡因子变为了2。

因为插入元素在最终形成不平衡节点的左侧的左侧,此时我们就可以进行不平衡的维护了。

右旋转之前:

右旋转之后:

  //add方法中,在计算完不平衡因子后
  if(balanceFacotor > 1 && getBalanceFactor(node.left) >= 0 )
      //右旋转
      return rightRotate(node);
    //右旋转
    // 对节点y进行向右旋转操作,返回旋转后新的根节点x
    //        y                              x
    //       / \                           /   \
    //      x   T4     向右旋转 (y)        z     y
    //     / \       - - - - - - - ->    / \   / \
    //    z   T3                       T1  T2 T3 T4
    //   / \
    // T1   T2
    private Node rightRotate(Node y) {
        Node x = y.left;
        Node T3 = x.right;
        //右旋转
        x.right = y;
        y.left = T3;
        //更新height    T1,T2,T3,T4在更新后仍然为叶子节点,height无需变化,先变化y后变化x
        y.height = Math.max(getHeight(y.left),getHeight(y.right)) + 1;
        x.height = Math.max(getHeight(x.left),getHeight(x.right)) + 1;
        return x;
    }

2、自平衡维护——RR左旋转

情况②插入元素在最终形成不平衡节点的右侧的右侧(RR)——旋转

旋转之前:

旋转之后:

    //在add方法中,计算出平衡因子之后
    if(balanceFacotor < -1 && getBalanceFactor((node.right)) <= 0)
            //左旋转
        return leftRotate(node);
//左旋转
    // 对节点y进行向左旋转操作,返回旋转后新的根节点x
    //    y                             x
    //  /  \                          /   \
    // T1   x      向左旋转 (y)       y     z
    //     / \   - - - - - - - ->   / \   / \
    //   T2  z                     T1 T2 T3 T4
    //      / \
    //     T3 T4
    private Node leftRotate(Node y) {
        Node x = y.right;
        Node T2 = x.left;
        x.left = y ;
        y.right = T2;
        //更新height
        y.height = Math.max(getHeight(y.left),getHeight(y.right)) + 1;
        x.height = Math.max(getHeight(x.left),getHeight(x.right)) + 1;
        return x;
    }

3、自平衡维护——LR

如左图所示,如果只进行简单的右旋转的话,那么8会作为根节点,而8<10且8<12,不能作为根节点,不能通过简单右旋转维护自平衡,又如右图,在新增节点4后,产生了LR的情况。如果简单地进行右旋转同样也是不行的【根节点5小于3和8】。

自平衡之前:                                                                                                         自平衡后:

    

在新插入节点之后,对于新插入的节点将会向上寻找找到第一个不平衡节点,不平衡产生于新插入的节点在不平衡节点左孩子的右侧,所以叫LR。处理思路:①首先对x进行左旋转,②转化为LL,③后对y节点右旋转维护自平衡

4、自平衡维护——RL

自平衡之前:

在新插入节点之后,对于新插入的节点将会向上寻找找到第一个不平衡节点,不平衡产生于新插入的节点在不平衡节点右孩子的左侧,所以叫RL。处理思路:①首先对x进行右旋转,②转化为RR,③后对y节点左旋转维护自平衡

猜你喜欢

转载自blog.csdn.net/itcats_cn/article/details/83512051
今日推荐