AVL 平衡树的旋转理解

AVL 平衡树的完全代码见 https://www.cnblogs.com/fanlumaster/p/13824006.html
首先我们明确一个定义:平衡因子:树的一个节点的左、右子树的高度差称为该节点的平衡因子。一棵 AVL 树的平衡因子只能有 0、-1、1 三种取值。

单旋转

LL 情况下的单旋转

左边是旋转前,右边是旋转后

20201016203344

LL情况:插入一个新节点到根节点的左子树(Left)的左子树(Left),导致根节点的平衡因子由 1 变为 2,如上图,在根节点 \(k_2\) 的左子树的左边插入节点 \(X\),导致 \(k_2\) 的平衡因子由 1 变成了 2。

为使树恢复平衡,我们把 \(k_1\) 变成这棵树的新根节点,因为 \(k_2\) 大于 \(k_1\),所以把 \(k_2\) 置为 \(k_1\) 的右子树,而原本在 \(k_1\) 的右子树的 \(Y\) 大于 \(k_1\) 且小于 \(k_2\),所以我们需要把 \(Y\) 置为 \(k_2\) 的左子树。这样一来,这棵树就重新恢复平衡。

简单来讲,我们只需要把 \(k_1\) 给“拎”上去,然后再把 \(Y\) 转移到 \(k_2\) 的左子树的位置就可以了。

一个旋转样例:

20201017001043

代码:

/* This function can be called only if K2 has a left child */
/* Perform a rotate between a node (K2) and its left child */
/* Update heights, then return new root */
//  LL 单旋转
static Position
SingleRotateWithLeft(Position K2)
{
    Position K1;

    // 旋转操作
    K1 = K2->Left;
    K2->Left = K1->Right;
    K1->Right = K2;

    // 更新节点的高度
    K2->Height = Max(Height(K2->Left), Height(K2->Right)) + 1;
    K1->Height = Max(Height(K1->Left), K2->Height) + 1;

    return K1;  /* New root 新的根节点 */
}

RR 情况下的单旋转

20201016203735

RR情况:和上面的类似,相当于对称的情况。

简单理解就是,把 \(k_2\) 给“拎”上去,然后 \(Y\) 根据相应的情况转移到 \(k_1\) 的右子树位置上。

代码:

/* This function can be called only if K1 has a right child */
/* Perform a rotate between a node (K1) and its right child */
/* Update heights, then return new root */
// RR 单旋转
static Position
SingleRotateWithRight(Position K1)
{
    Position K2;

    // 旋转操作
    K2 = K1->Right;
    K1->Right = K2->Left;
    K2->Left = K1;

    // 更新节点高度
    K1->Height = Max(Height(K1->Left), Height(K1->Right)) + 1;
    K2->Height = Max(Height(K2->Right), K1->Height) + 1;

    return K2;  /* New root */
}

双旋转

LR 情况下的双旋转

20201016212009

结合上面的图解,可以发现,LR 情况下的双旋转其实是两个单旋转:

  • 先对 \(k_1\) 进行 RR 单旋转,即,把 \(k_2\) 给“拎”上来
  • 然后对 \(k_3\) 进行 LL 单旋转,即,把 \(k_2\) 给“拎”上来

代码:

/* This function can be called only if K3 has a left */
/* child and K3's left child has a right child */
/* Do the left-right double rotation */
/* Update heights, then return new root */
// LR 双旋转
static Position
DoubleRotateWithLeft(Position K3)
{
    /* Rotate between K1 and K2 */
    K3->Left = SingleRotateWithRight(K3->Left); // 先进行 RR 旋转

    /* Rotate between K3 and K2 */
    return SingleRotateWithLeft(K3); // 然后进行 LL 旋转
}

RL 情况下的双旋转

20201016213529

和 LR 类似,RL 情况下的双旋转也是两个单旋转:

  • 先对 \(k_3\) 进行 LL 单旋转,把 \(k_2\) 给“拎”上来
  • 然后对 \(k_1\) 进行 RR 单旋转,把 \(k_2\) 给“拎”上来

代码:

/* This function can be called only if K1 has a right */
/* child and K1's right child has a left child */
/* Do the right-left double rotation */
/* Update heights, then return new root */
// RL 双旋转
static Position
DoubleRotateWithRight(Position K1)
{
    /* Rotate between K3 and K2 */
    K1->Right = SingleRotateWithLeft(K1->Right);

    /* Rotate between K1 and K2 */
    return SingleRotateWithRight(K1);
}

一个例子

插入 3,2,3,在插入 1 的时候进行 LL 单旋转

20201017001733

插入 5,RR 单旋转

20201017002430

插入 6,RR 单旋转

20201017002937

插入 7,RR 单旋转

20201017003945

插入 15,16,RL 双旋转

20201017005028

插入 14,RL 双旋转

20201017010143

插入 13,RR 单旋转

20201017011112

插入 12,LL 单旋转

20201017011736

插入 11,10,8,略过过程,其中,插入 11 和 10 都需要单旋转,插入 8 不需要旋转

20201017012240

插入 9,LR 双旋转

20201017013127

posted @ 2020-10-17 01:38  模糊计算士  阅读(318)  评论(0编辑  收藏  举报