zoukankan      html  css  js  c++  java
  • 我的新发现:AVL树旋转的一个特性

    关于AVL树旋转的代码网络上铺天盖地。

    一些经典的实现方法如下:

    AVLTree SingleLeftRotation(AVLTree A)
    {
        AVLTree B = A->left;
        A->Left = B->Right;
        B->Right = A;
        A->Height = Max(GetHeight(A->Left), GetHeight(A->Right)) + 1;
        B->Height = Max(GetHeight(B->Left), A->Height) + 1;
        
        return B;
    }
    
    AVLTree DoubleLeftRightRotation(AVLTree A)
    {
        A->Left = SingleRightRotation(A->Left);
        
        return SingleLeftRotation(A);
    }

    这段代码看上去很有美感,短小精悍。

    不过我发现了一点,其实单向旋转不用使用Max比较左右节点的高度,因此我是这样实现的:

    PNODE SingleRightRotation(PNODE A) // 右右旋转,返回旋转部分的根节点
    {
        PNODE B = A->right;
        A->right = B->left;
        B->left = A;
         
        A->height = Height(A->left) + 1; // 这里没有通过比较更新A的高度,因为BL不会大于AL(否则在之前它们就不平衡了)
        B->height = A->height + 1; // 与上述理由类似
         
        return B;
    }

    但是随之带来了一个问题,就是双向旋转依赖于单向旋转的实现,因此在有双向旋转操作的前提下,代码还能正确的做它应该做的事情吗?

    我试着证明了一下,我的代码在无论单向还是双向旋转的情况下都是正确的:

    这是我在PAT上用自己的代码AC之后与何钦铭老师的对话:

    我:

    我在pat上做这道题:04-树3. Root of AVL Tree (25)的时候,单向旋转是这样做的:
    PNODE SingleRightRotation(PNODE A) // 右右旋转,返回旋转部分的根节点
    {
        PNODE B = A->right;
        A->right = B->left;
        B->left = A;
         
        A->height = Height(A->left) + 1; // 这里没有通过比较更新A的高度,因为BL不会大于AL(否则在之前它们就不平衡了)
        B->height = A->height + 1; // 与上述理由类似
         
        return B;
    }
    正如注释上所写,我认为A的高度不用通过比较来更新,我觉得其高度和BL没有关系,因为BL不可能大于AL,否则它们在之前就不平衡了,就早早的经过旋转达到平衡了。这样做以后提交正确了,因此,想跟老师确认一下,这样理解正确吗?
    

    何钦铭老师:

    你提的问题很好呀。在仅针对LL、RR旋转的情况下,你的做法是可以的(不需要max的比较)。但LR和RL也是通过两次调用SingleRightRotation和SingleLeftRotation来实现的,在这种情况下,你的做法是否仍然有效需要再仔细琢磨一下。
    

    我:

    恩恩,这个测试数据比较少,不能通过AC来证明它是正确的,但是我在理论上证明了一下,似乎可行:
    首先,对于单向旋转,只需要说明一种情况正确即可,以RR旋转为例:
        AL与BL的关系只有三种,即:AL = BL;AL = BL+1;AL = BL+2 综合上述三种情况,AL ≥ BL。因此无须比较。
        由于节点是插在BR上的,在加上A左右两侧高度差为2这个条件,可得AL+1 = BR,但是旋转之后B为根了,所以AL+A = BR,因此更新B的高度使取AL+A或者BR两者皆可(下面会说明双向旋转的时候不可以用BR),因为它们是相等的,也无须比较。
    其次,对于双向旋转,以LR旋转为例:
        第一步操作是以B为根的RR旋转,而此时的B是平衡的(因为不平衡的只有A,如果不考虑A的父节点的话),要证明它的旋转也不需要比较,需要证明BL ≥ CL,而这一点由B是平衡的来保证。此外,还需要证明BL+B ≥ CR,不管插入的节点在CL还是CR,这一点同样可以由B是平衡的来保证(这里的CR相对应与单向旋转的BR,因为存在BL+B > CR的情况(与单向旋转中AL+A = BR不同),这也是单向旋转中不能用BR来更新B的高度的原因)。
        接下来的操作是以A为根的LL旋转,此时C变成了A的左子树,B成了C的左子树,而且由第一步的分析可以知道各个节点的高度都得到了有效的更新,但是从层次上看, BL降了一层,CL所在的层次不变,而CR的层次上升了一层,在这种状况下要证明无须比较的LL旋转是正确的,就比较困难了,但是不难发现,只要证明不平衡的节点仍然只有A(如果节点插入到CL,那么A肯定不平衡,如果节点插入到CR,可以由BL降了一层来保证A仍然是不平衡的,其实,只要BL降了,不管节点插在CL还是CR,A就肯定不平衡了)就可以保证LL旋转的正确性,但是第一步操作有没有造成不平衡的节点产生也是需要考虑的一个方面,我的想法是不用管它,把RR旋转后的结果画出来,发现要证明LL旋转是正确的只要证明A+AR可以使得C所在的高度达到最大就可以了,这一点可以由AR不会比BL、CL、CR(CL或CR代表插入之后的高度)三个中的任何一个小来保证。
    
    不知道这样的分析是否正确,不过,我愿意相信它,因为人类往往偏向于相信自己的直觉....
    

    如果它是正确的话,那么在AVL树中就可以利用它提速、省空间,虽然微不足道,但是属于自己发现的,心里还是很爽的:P

    All Rights Reserved.
    Author:海峰:)
    Copyright © xp_jiang. 
    转载请标明出处:http://www.cnblogs.com/xpjiang/p/4491250.html
    以上.
  • 相关阅读:
    2-4 递增链表的插入 链表
    KMPnext数组自看
    Shortest Prefixes POJ
    Xor Sum HDU
    Immediate Decodability HDU
    Repository HDU
    "strcmp()" Anyone? UVA
    Remember the Word UVALive
    A Magic Lamp HDU
    Check Corners HDU
  • 原文地址:https://www.cnblogs.com/xpjiang/p/4491250.html
Copyright © 2011-2022 走看看