Root of AVL Tree (25 分)---AVL树的证明式分析

(第一篇)记PAT习题,AVL树的原理的见解

04-树5 Root of AVL Tree (25 分)

 

  

 题目意思就是给出一个N,接着插入N个值,以AVL树的方式插入,然后求AVL树的根节点

 

首先是树的节点结构体,网上有人实现是节点本身储存了一个额外信息就是树高,而不存储平衡因子这个信息。而我的实现是储存平衡因子不储存树高。

typedef struct NODE {
    int n;
    int bl;
    NODE* l;
    NODE* r;
}NODE;                //我设定的是右树高减左树高

 

  AVL树的平衡调整方式有4种,分别是LL旋转,RR旋转,LR旋转,RL旋转。RR与LL旋转是对称的,LR与RL是对称的。

我不知道有没有人是和我一样的想法的,AVL树的四种平衡调整的一个难点在于找到“失衡节点”,如何找到失衡节点我觉得是一个很有探索空间的地方。

  我的实现方式是:对于任意一个失衡节点,它只关心它自己的失衡原因,对于一个非失衡节点,它只要返回(上报给父节点)它的高度变化原因。

为什么这样?    

 

 

 

   观察这张图,红线左侧,是“RR失衡模板”---------6失衡了(总树高4层是为了更有通用性)。这棵树调整为红线右侧即可恢复平衡。

  其中没有标数字的节点代表旋转前后不影响的节点,比如红色节点,因为在6的右侧,故比6大,所以调整后,6的右子树空缺,若红色节点存在,则接到6的右树上。

  
  模拟一下RR旋转检测与调整过程:比如我们插入的是元素12,找到位置之后,插入12。对于12本身,因为它是从无到有,所以它的平衡因子是0,上报(递归的返回)父节点我----》作为一棵树,

增高了,并且增高原因是由于自己(从无到有)。

      返回到10,10发现右树来报它增高了,增高原因是右树本身。检测自己平衡因子,原来是0,现在是1.于是它又上报它的父亲节点,报告,我也增高了

(为什么用又?)增高原因是我的右儿子。

      现在到8,它的平衡因子原来是0,右树报告它增高了,增高原因是它的右儿子。8检查自己的平衡因子,从0到1,仍未失衡,又往上报告---》我增高了,增高原因是我的右子树(至于是右子树自己还是右子树的左或右,其父并不care)。

      ok,到了6节点,它发现他的平衡因子本来就是1,右树增高,+1,变成2.于是疾呼,来人啊!我失衡了!-----》源于右子树(故R_旋转),而右子树报告其右树长高,故为RR旋转。

 

RR旋转:对于原根节点,简单来说就是右树提上去,自己左沉,原右树若是有左树,那便接到自己右树。因为自己的右树任何元素都比自己大,故肯定不亏。

     对于RR操作,很多源码,把上面这行翻译一下就可以了。重点是关于重新调整平衡因子!!

    看图  ↑,假设蓝框部分树高n,(其实就是原根的左树),那么,因为根RR失衡   所以,必然是此刻右树的高度比左树高2,所以绿框部分树高为n+2,因为节点只能一个一个插入。麻烦节点在

一次插入之时只会有一个 ,-—》12。10的左子肯定为空,否则在插入10的左子时就已经失衡了。再者,RR失衡,8对6的返回值是“右树增高导致我增高”。也说明8右树必然高于左树,若左树右树一样高,说明插入之后它的高度未变,则不会给6汇报自己增高了,而8未失衡,故8一定是右树比左树高1。所以灰色框里树高一定是n+1,橙色框里树高一定为n。

    再看图右侧,旋转之后。10和自己的子节点未发生变化。故平衡因子不变。6的左树是蓝框,高n,右侧是橙框,高n,故6的新平衡因子是0。同理,8的左树必然高n+1,同右树,8的平衡因子也一定为0。

 

    

#include<iostream>
#define CAN_NOT -5
#define REPEAT -4
#define SHOULD_NOT_BE_THERE -3
enum status {
    NON,ME,BYLEFT,BYRIGHT
};//枚举表示增高原因,未增高,自己,左,右





typedef struct NODE {
    int n;
    int bl;
    NODE* l;
    NODE* r;
}NODE;                //我设定的是右树高减左树高


NODE* AVL=NULL;

void RR(NODE* &root);
void RL(NODE* &root);
void LL(NODE* &root);
void LR(NODE* &root);


status Insert(NODE* &root,int a) {
    status ret;
    if (root == NULL) {
        root = new NODE;
        root->bl = 0;
        root->l = NULL;
        root->r = NULL;
        root->n = a;
        return ME;
    }//是我自己导致我的树增高,返回ME
    else if (a > root->n) {    //往右子树插入
        ret = Insert(root->r, a);

        if (ret == NON)
            return NON;
        else {    //右树增高,待处理
            
            ++root->bl;
            if (root->bl <= 0)
                return NON;
            else if (root->bl < 2) {
                return BYRIGHT;
            }
            else {            //自己失衡,原因RR或RL,
                if (ret == BYRIGHT) {
                    RR(root);
                    return NON;
                }
                else if(ret==BYLEFT){
                    RL(root);                //RL旋转
                    return NON;
                }
                else {
                    exit(CAN_NOT);//ME导致失衡,不可能
                }
            }
        }
    }
    else if (a == root->n) {
        exit(REPEAT);        //不允许相等
    }
    else {
        ret = Insert(root->l, a);
        if (ret == NON) {
            return NON;
        }
        else{
            --root->bl;
            if (root->bl >= 0) {
                return NON;        //依然OK
            }
            else if(root->bl>-2) {
                return BYLEFT;
            }
            else {
                if (ret == BYLEFT) {    //LL旋转
                    LL(root);
                    return NON;
                }
                else if(ret==BYRIGHT) {
                    LR(root);
                    return NON;
                }
                else {
                    exit(CAN_NOT);//ME导致失衡,不可能
                }
            }            
        }
    }
    exit(SHOULD_NOT_BE_THERE);//不应该走到这个位置 好长的insert函数
}






int main() {
    int n;
    int tmp;

    NODE* AVL=NULL;

    std::cin >> n;
    for (int i = 0; i < n; ++i) {
        std::cin >> tmp;
        Insert(AVL, tmp);
    }

    std::cout << AVL->n;


    return 0;
}




void RR(NODE* &root) {        //RR旋转
    
    NODE* tmp = root;        //root变更为右树
    root = tmp->r;
    tmp->r = root->l;
    root->l = tmp;

    //调整平衡因子
    tmp->bl -= 2;        //推算可得
    root->bl = 0;        //大致是,因2破坏,设左树高n,右树高n+2,RR破坏,右右树高n-1,右树未破坏且右右高,右左高n+1。。。


    return;
}


void LL(NODE* &root) {//纸笔推算一下,失衡的一些确定推断
    NODE* tmp = root;
    root = tmp->l;
    tmp->l = root->r;
    root->r = tmp;

    tmp->bl += 2;        
    root->bl = 0;    


    return;
}

void RL(NODE* &root) {

    NODE* tmp = root;
    root = tmp->r->l;

    tmp->r->l = root->r;
    root->r = tmp->r;
    tmp->r = root->l;
    root->l = tmp;        //RL旋转复杂一些,不过都可以通过设树高论证原理
                            
    if (root->bl == -1) {        //重新设置平衡因子,比较繁琐,推论可知
        root->r->bl = 1;
        root->bl = 0;
        root->l->bl = 0;
    }
    else if (root->bl == 1) {
        root->r->bl = 0;
        root->bl = 0;
        root->l->bl = -1;
    }
    else {
        root->l->bl = 0;
        root->r->bl = 0;
    }


    return;
}


void LR(NODE* &root) {

    NODE* tmp = root;
    root = tmp->l->r;

    tmp->l->r = root->l;
    root->l = tmp->l;
    tmp->l = root->r;
 root->r = tmp;  if (root->bl == -1) { root->r->bl = 1; root->bl = 0; root->l->bl = 0; } else if (root->bl == 1) { root->r->bl = 0; root->bl = 0; root->l->bl = -1; } else {//LR旋转导致失衡,且root ->bl为0,说明LR是新产生的节点 root->l->bl = 0; root->r->bl = 0; } return; }

    LL旋转的分析基本等同RR旋转。代码里有些无用的本来用于调试可能的错误原因,结果意外的好,就改了一个bug就过了。

     后面是对LR的分析。下次补上。第一次写,试试。

补充:

  

如图所示,是RL破坏的旋转示意图。最重要的恢复平衡步骤就是把RL节点移动到根节点,

    

  NODE* tmp = root; //tmp用来保存原根节点
  root = tmp->r->l; //root变成RL节点      
只把根变为RL是不够的,RL如果有儿子,那么就由图中的3和6节点领养 -->根据搜索树规律:黄色节点一定比3大,红色节点一定比6小
 NODE* tmp = root;
    root = tmp->l->r;

    tmp->l->r = root->l;//3的右儿子接上黄色节点
    root->l = tmp->l;//只是注意一点,要先将6(原根)的左子树接到新根的左保存
    tmp->l = root->r;   //6的左儿子变为红色节点
    root->r = tmp;    

平衡因子的调整:
类似LL旋转的分析,设绿框高n,蓝框必高n+2,红框必高n,棕色框必高n+1。但是比如6的平衡因子要判断一下先,
因为左子树最高为n,也可能小于n,要根据原来5的平衡因子决定
  比如-1说明是原来5的左树高,右树低,1相反,0说明5是新节点,
   if (root->bl == -1) {
        root->r->bl = 1;
        root->bl = 0;
        root->l->bl = 0;
    }
    else if (root->bl == 1) {
        root->r->bl = 0;
        root->bl = 0;
        root->l->bl = -1;
    }
    else {//LR旋转导致失衡,且root ->bl为0,说明LR是新产生的节点
        root->l->bl = 0;
        root->r->bl = 0;
    }
RL和LR操作相反。
AVL树的删除操作以后再说吧。

 

只是注意一点,要先将6的左节点空出

转载于:https://www.cnblogs.com/slowman/p/9892689.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值