代码改变世界

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

2010-10-08 23:31  Clingingboy  阅读(3557)  评论(0编辑  收藏  举报

 

 

继续讨论旋转

为了方便讨论是做点记号

  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); } } }
话题全是紧扣着旋转和平衡因子,还未完...