• 平衡二叉树(AVL)实现(2)


    继续讨论旋转

    为了方便讨论是做点记号

    1. X为插入的节点
    2. P为旋转轴(P有时候为X的父节点如LL,RR旋转;P有时候也为X,如LR,RL旋转)
    3. R为平衡因子绝对值=2的节点

    看下面四种情况

    image

    LL旋转

    当三个节点处于一条直线,并均是左节点时,需要以中间的节点为旋转轴向右侧(顺时针)旋转一次

    image

    1. 使得C成为B的右子节点
    2. B代替C的位置
    3. B的右子节点成为C的左子节点
      private Node LL(Node root) {
          Node rootNext = root.Left;
          root.Left = rootNext.Right;
          rootNext.Right = root;
          return rootNext;
      }

    RR旋转

    当三个节点处于一条直线,并均是左节点时,需要以中间的节点为旋转轴向左侧(逆时针)旋转一次

    image

    1. 使得A成为B的左子节点
    2. B代替A的位置
    3. B的左子节点成为A的右子节点
    private Node RR(Node root) {
        Node rootNext = root.Right;
        root.Right = rootNext.Left;
        rootNext.Left = root;
        return rootNext;
    }

    LR旋转

    P为R的左节点,X为P右节点
    当三个节点处于一条直线,并均是左节点时,需要以插入的节点为旋转轴向左侧(逆时针)旋转一次,然后向右侧(顺时针)旋转一次,即先做RR旋转再做一次LL旋转

    image

    执行RR旋转(参照RR旋转规则)

      1. 使得A成为B的左子节点
      2. B代替A的位置
      3. B的左子节点成为A的右子节点

    注意现在是以B为旋转轴,所以C位置没有发生变化

    现在可以执行LL旋转了(注意现在是以B为旋转轴了),同LL旋转一样
    看图片左边部分

    image

    执行LL旋转

    那么LR的代码如果借助LL和RR的方法则变得非常简单

    private Node LR(Node root)     
        root.Left = RR(root.Left);
        return LL(root);
    }

    RL旋转

    跟LR相关执行LL,RR旋转

    image

    private Node RL(Node root) {
        root.Right = LL(root.Right);
        return RR(root);
    }

    LL,RR旋转后改变平衡因子

    现实中旋转后我们马上可以得出平衡因子发生了变化,但在程序中我们必须手动对平衡因子做出改动

    比如LL旋转前

    1. RH(R的平衡因子)=2
    2. PH=1
    3. X=0

    旋转后

    1. RH=0
    2. PH=0
    3. X=0

    LL

    即当P的平衡因子为1时

    if (rootNext.BF == 1)
    {
        root.BF = 0;
        rootNext.BF = 0;
    }

    RR

    即当P的平衡因子为-1时

    if (rootNext.BF == -1)
    {
        root.BF = 0;
        rootNext.BF = 0;
    }

    双旋转的合并

    比如LR旋转

    将LL和RR的代码合并在一起

    private Node LR(Node root) {
        Node rootLeft = root.Left;
        //RR(rootLeft);
        Node pRight = rootLeft.Right;
        rootLeft.Right = pRight.Left;
        pRight.Left = rootLeft;
    
        root.Left = pRight;
    
        //LL(root);
        rootLeft = root.Left;
        root.Left = rootLeft.Right;
        rootLeft.Right = root;
    }

    上面的代码让人看起来思路非常的清晰,但由于是程序,所以可以简化

    rootLeft就是pRight

    root.Left不要赋值两次

    以下是改进

    private Node LR(Node root) {
        Node rootLeft = root.Left;
        //RR(rootLeft);
        Node pRight = rootLeft.Right;
        rootLeft.Right = pRight.Left;
        pRight.Left = rootLeft;
        //LL
        root.Left = pRight.Right;
        pRight.Right = root;
    }

    LR旋转后的平衡因子

    有三种情景
    1.P的平衡因子为0

    如下图
    image

    R的平衡因子变为0,P的父节点平衡因子变为0,自身平衡因子变为0

    2.P的平衡因子为-1

    image

    R的平衡因子变为-1,P的父节点平衡因子变为0,自身平衡因子变为0

    3.P的平衡因子为1

    image

    R的平衡因子变为0,P的父节点平衡因子变为1,自身平衡因子变为0

    switch (pRight.BF) {
        case 0:
            root.BF = 0;
            rootLeft.BF = 0;
            break;
        case 1:
            root.BF = -1;
            rootLeft.BF = 0;
            break;
        case -1:
            root.BF = 0;
            rootLeft.BF = 1;
            break;
    }
    pRight.BF = 0;

    旋转的选择

    当R的绝对值等于2时,如果等于2说明树的左边加入了一个节点,反之则是右侧节点.

    当R==2时,则检查R的左侧节点的平衡因子,有两种情况1或-1,如果是1的话,则LL旋转,如果是-1的话则LR旋转

    反之当R==-2时,情况则刚好相反

    private bool RotateSubTree(int bf)
    {
            Node root = path[currentIndex], newRoot = null;
         if (bf == 2)     {
            int leftBF = root.Left.BF;
            if (leftBF == -1)         {
                newRoot = LR(root);
            }
            else if (leftBF == 1)
            {
                newRoot = LL(root);         }
        }
        if (bf == -2)     {
            int rightBF = root.Right.BF;         
    if (rightBF == 1) { newRoot = RL(root); } else if (rightBF == -1) { newRoot = RR(root); } } }
    话题全是紧扣着旋转和平衡因子,还未完...
  • 相关阅读:
    实现Email传送
    用角色来管理权限
    最大在线人数统计
    解决DataGrid显示时间格式问题
    3秒后自动跳转
    ASP.NET页面的处理顺序
    乱码问题
    动态生成button并关联其onclick事件
    获取用户计算机信息
    鼠标滚轮缩放图片(js)
  • 原文地址:https://www.cnblogs.com/Clingingboy/p/1846203.html
Copyright © 2020-2023  润新知