Avl - tree 平衡二叉树

都说平衡二叉树又臭又长,其实写起来不算长,不到200行,就是调试起来太麻烦了,熟练了之后应该会好点,今天整整调试了一下午,==变成了=,之后还有因为把插入新节点和修改平衡因子放在一个循环里面,后来发现插入路径上的有些节点的平衡因子不需要修改,结果调试了一下午 QwQ……

要注意的一点是AVL不是每插入一个节点就需要修改整个树的平衡因子,其实只需要修改在插入路径上最后一个平衡因子不为0的节点(后面称”修改节点”)的所有后代节点(因为平衡因子不为0的话,那么以它为根节点树肯定不是一颗满二叉树,那么就一定能通过旋转让他保持插入节点之前的深度,从而不会影响上面的节点)

旋转有四种,两种单旋转(左左,右右),两种双旋转(左右,右左),

1.头文件

struct node
{
    int va, fb;  //权,平衡因子
    node *left, *right; //左右孩子
};

2.左左旋转
指插入的节点在修改节点的左孩子的左子树上,具体旋转看图.
这里写图片描述
具体代码如下:

node* llrotate(node *root)  //左左
{
    node *pa=root;
    node *pb=root->left;
    pa->left=pb->right;
    pb->right=pa;
    pa->fb=pb->fb=0;
    return pb;
}

3.右右旋转
指插入的节点在修改节点的右孩子的右子树上,具体旋转看图.
这里写图片描述
具体代码如下:

node* rrrotate(node *root) //右右
{
    node *pa=root;
    node *pb=root->right;
    pa->right=pb->left;
    pb->left=pa;
    pa->fb=pb->fb=0;
    return pb;
}

4.左右旋转
指插入的节点在修改节点的左孩子的右子树上,这里和单旋转有点不同,因为需要旋转两次,所以要把修改节点的左孩子的右孩子表示出来,选以修改节点的左孩子为根做一次右右旋转,然后做一次左左旋转,,其实这里的插入有两种情况,插入节点可能在C(图)的左子树或者C的右子树,不过旋转方法是一样的只是修改平衡因子的时候有点不同,具体旋转看图.
这里写图片描述
具体代码如下:

node* lrrotate(node *root) //左右
{
    node *pa=root;
    node *pb=root->left;
    node *pc=root->left->right;
    pb->right=pc->left;
    pc->left=pb;
    pa->left=pc->right;
    pc->right=pa;
    if(pc->fb==1)  //插入节点C的左子树上
    {
        pa->fb=-1;
        pb->fb=pc->fb=0;
    }
    else if(pc->fb==-1) //插入节点在C的右子树上
    {
        pb->fb=1;
        pa->fb=pc->fb=0;
    }
    else  //插入节点就是C
        pa->fb=pb->fb=pc->fb=0;  
    return pc;
}

5.右左旋转
指插入的节点在修改节点的右孩子的左子树上,这里和单旋转有点不同,因为需要旋转两次,所以要把修改节点的右孩子的左孩子表示出来,选以修改节点的左孩子为根做一次左左旋转,然后做一次右右旋转,,其实这里的插入有两种情况,插入节点可能在C(图)的左子树或者C的右子树,不过旋转方法是一样的只是修改平衡因子的时候有点不同,具体旋转看图.
这里写图片描述
具体代码如下:

node* rlrotate(node *root) //右左
{
    node *pa=root;
    node *pb=root->right;
    node *pc=root->right->left;
    pb->left=pc->right;
    pc->right=pb;
    pa->right=pc->left;
    pc->left=pa;
    if(pc->fb==1) //插入节点C的左子树上
    {
        pb->fb=-1;
        pa->fb=pc->fb=0;
    }
    else if(pc->fb==-1)//插入节点C的左子树上
    {
        pa->fb=1;
        pb->fb=pc->fb=0;
    }
    else  //插入节点就是C
        pa->fb=pb->fb=pc->fb=0;
    return pc;
}

6.插入节点部分
插入的过程要保存修改节点 修改节点的父节点

node* Avl(node *head,int x)
{
    node *newnode=new node;
    newnode->fb=0;
    newnode->va=x;
    newnode->left=newnode->right=NULL;
    if(!head)          //空树
        return newnode;
    node *per,*fper,*flag,*fflag;  //修改节点,修改节点的父节点,临时节点,临时节点的父节点
    fper=fflag=NULL;
    per=flag=head;
    while(flag)
    {
        if(flag->fb!=0){   //可以做修改节点
            per=flag;    //保存
            fper=fflag;  //保存父节点方便旋转后连接旋转部分
        }
        fflag=flag;  //保存父节点用于插入新节点
        if(flag->va>x)
            flag=flag->left;
        else
            flag=flag->right;
    }
    if(fflag->va>x)  //插入新节点
        fflag->left=newnode;
    else
        fflag->right=newnode;
    flag=per;
    while(flag!=newnode)  //修改平衡因子,从修改节点开始,不可以和插入同步,因为插入时修改节点不确定
    {
        if(flag->va>x){ //定义左插平衡因子加一
            flag->fb++;
            flag=flag->left;
        }
        else{        //减一
            flag->fb--;
            flag=flag->right;
        }
    }
    if(per->fb!=2&&per->fb!=-2) //树仍然保持平衡
        return head;
    else if(per->fb==2)//插入点在修改节点的左孩子的子树上
    {
        if(per->left->fb==1)  //左子树
            flag=llrotate(per);
        else                  //右子树
            flag=lrrotate(per);
    }
    else if(per->fb==-2)//插入点在修改节点的右孩子的子树上
    {
        if(per->right->fb==1) //左子树
            flag=rlrotate(per);
        else                  //右子树
            flag=rrrotate(per);
    }
    if(!fper)  //修改节点的父节点为空,表示修改节点是原树的根
        return flag;
    if(fper->left==per)
        fper->left=flag;
    else
        fper->right=flag;
    return head;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

achonor

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值