平衡搜索二叉树-指针粗糙版

平衡搜索二叉树-指针粗糙版


两个旋转,四种情况!

平衡因子

平衡因子BF(Balance Factor),等于左子树的高度减去右子树。

  • 规定: 空节点的高度为0,叶子节点的高度为1。

  • |BF|= 0时

​ 很平衡,没什么问题。此时以此结点为根的子树平衡,且其高度不变。因此从根结点到此结点的路径上各结点子树高度不变,从而各结点的平衡因子不变。此时可结束重新平衡过程。

  • |BF|= 1时

​ 此时虽然还是满足平衡条件(|BF|<=1)的,但高度已经发生变化,所以应该继续考察当前节点的父节点。

  • |BF|= 2时

​ 此时就不满足平衡条件了,要开始旋转了。

结构体定义如下:

typedef struct node{
    int val;
    int h;
    struct node* left;
    struct node* right;
}avlnode;
typedef avlnode* avlptr;

//求平衡因子的函数。
int getF(avlptr root){
    return  (root->left!=nullptr?root->left->h:0 ) 
        	- 
        	(root->right!=nullptr?root->right->h:0);
}

两个旋转

主要就是左旋和右旋两种操作。

左旋:

leftRotate

如图所示,左旋是为了解决右子树过高问题的操作。注意这边不要在意图上给出的平衡因子,因为他这个图这边好像还把平衡因子算错了,应该是-2和-1,这边主要考虑的是为什么左旋,原因其实就是因为右边高了,要左旋。

个人一点粗浅的看法:很多兄弟在学这个的时候会感觉很晕,其实就是把左旋也算进失衡的那四种情况里面了,实际上左旋和右旋只是两种旋转的方法,我们在不同的情况中运用不同的旋转方法,仅此而已

左旋的操作流程是: 找出失衡的结点A,因为是右边过高,所以A的右子树B将会是新的树的根,然后A成为B的左子树,这个旋转操作在B有左子树的时候会比较复杂,需要把B的左子树作为A的右子树

avlptr leftRotate(avlptr root){
    avlptr right = root->right;         //right将是新树的根
    root->right = right->left;			//把B的左子树作为A的右子树
    right->left = root;					//A成为B的左子树

    right->left->h = getH(right->left); 
    right->h = getH(right);
    //这两行代表的是更新B和A的高度或者平衡因子。具体实现操作因人而异,我这边是自己写了个求高度的函数
    //这种记录高度的方式在求平衡因子的时候要多一步麻烦一点,但是会更直观一点。
    //存在直接修改平衡因子的方法,但是会比较难搞懂一点。
    
    return right;						//返回新的树根结点
}

int getH(avlptr root){
    if(root==nullptr){
        return 0;
    }
    else{
        return max(
                   root->left!=nullptr?root->left->h:0		//左子树高度
                   ,
                   root->right!=nullptr?root->right->h:0	//右子树高度
                  )+1;										//最高子树高度+1
    }
}
//本来应该用递归的方法来求高度的,但是我为了节省时间复杂度,在插入的时候就更新节点的高度,所以在调整树需要旋转的时候,能保证相关树的高度都是更新过的,以下是能保证完全正确并且好理解的一种求高度的递归函数
int getHeight(avlptr root) {
    if(root == NULL) {
        return 0;
    } else {
        return max(getHeight(root->left),getHeight(root->right)) + 1;
    }
}

右旋:

右旋和左旋完全相反,只要能懂一个,另一个就能懂,不再赘述

rightRotate

avlptr rightRotate(avlptr root){
    avlptr left = root->left;
    root->left = left->right;
    left->right = root;

    left->right->h = getH(left->right);
    left->h = getH(left);
    return left;
}

四种失衡情况

前面说到,当|BF|>1时,就能判断出这个结点失衡了,那么就对目前的情况进行分类,就能得到四种情况,也就是我们常听说的LL LR RR RL四种情况。

BF=2时

因为是正数,所以是左边子树高了,所以要观察左子树的情况

bf(left)=1 ---- LL

可以知道是左子树的左子树高了,也就是说新插入的结点是插入在左子树的左子树上,这就是LL情况,两个L的意思就是这个。

  • 对于这种情况,只要做一次右旋即可。
bf(left)=-1 ---- LR

可以知道是左子树的右子树高了,也就是说新插入的结点是插入在左子树的右子树上。

在这里插入图片描述

  • 因为是左子树的右边高了,所以应该对左子树进行一次左旋。回到根节点,是左子树高了,所以要对根节点进行一次右旋

BF=-2时

因为是负数,所以是右边子树高了,所以要看向右子树的情况

bf(left)=-1 ---- RR

可以知道是右子树的右子树高了,也就是说新插入的结点是插入在右子树的右子树上,这就是RR情况,两个R的意思就是这个。

  • 对于这种情况,只要做一次左旋即可。
bf(left)=1 ---- RL

可以知道是右子树的左子树高了,也就是说新插入的结点是插入在右子树的坐子树上。

在这里插入图片描述

  • 因为是右子树的左边高了,所以应该对右子树进行一次右旋。回到根节点,是右子树高了,所以要对根节点进行一次左旋

以上便是四种失衡情况即应对方式。

插入操作

avlptr insert(avlptr root,int val){
    if(root==nullptr){
        root = init();
        root->val = val;
        root->h=1;
        root->left = nullptr;
        root->right = nullptr;
        return root;
    }//初始化操作,记得要把指针变量赋值好,要不然可能会出奇奇怪怪的问题。
    else{//插入和普通二叉搜索树没有什么差别
        if(root->val==val){
            //有些题目会保证不重复。
        }
        else if(root->val > val){
            root->left = insert(root->left,val);
        }else{
            root->right = insert(root->right,val);
        }
        //在插入完成后的操作
        root->h = getH(root);    	//调整高度
        root = rebalance(root);		//对结点进行平衡操作,由于是递归,平衡操作会从插入结点回溯到根节点
    }
    return root;
}
avlptr rebalance(avlptr root){
    int factor = getF(root);		//求出这个树的平衡因子
    if(factor>1){					//BF=2 左高 看向左节点
        if(getF(root->left)>0){		
            //LL
            return rightRotate(root);
        }else if(getF(root->left)<=0){
            //LR
            root->left = leftRotate(root->left);
            return rightRotate(root);
        }
    }
    else if(factor<-1){				//BF=-2 右高 看向右节点
        if(getF(root->right)<0){
            //RR
            return leftRotate(root);
        }
        else if(getF(root->right)>=0){
            //RL
            root->right = rightRotate(root->right);
            return leftRotate(root);
        }
    }
    else{							//平衡状态 直接返回就好
        return root;
    }
}

删除操作

删除操作是比较复杂的,因为搜索二叉树的删除本身就比较复杂,涉及到先序节点和后继节点的查找。可以去这个博客学习如何删除二叉搜索树中的节点。同样也是用回溯的思想进行平衡操作。

void del(int val,avlptr* rootptr){
    avlptr root = *rootptr;
    if(root==nullptr){
        return ;
    }
    if(val<root->val){
        del(val,&(root->left));
    }
    else if(val>root->val){
        del(val,&(root->right));
    }
    else{//val==root->val  找到了
        if(root->left==nullptr&&root->right==nullptr){
            *rootptr = nullptr;
            return;
        }
        else if(root->left!=nullptr){
            avlptr ps = preSuccessor(root);
            root->val = ps->val;
            del(ps->val,&(root->left));
        }else{
            avlptr s = successor(root);
            root->val =  s->val;
            del( s->val,&(root->right));
        }
        
    }
    (*rootptr)->h = getHeight(*rootptr);
    *rootptr = rebalance(*rootptr);
}

// NOTICE : 默认左子树不为空!!
avlptr preSuccessor(avlptr root){
    if(root==nullptr){
        return nullptr;
    }else{
        avlptr left = root->left;
        while(left->right){
            left = left->right;
        }
        return left;
    }
}

// NOTICE : 默认右子树不为空!!
avlptr successor(avlptr root){
    if(root==nullptr){
        return nullptr;
    }else{
        avlptr right = root->right;
        while(right->left){
            right = right->left;
        }
        return right;
    }
}

全部代码

说实话用指针是真的难受,不知道考试的时候能不能用链式数组代替吧,指针是真的难受…还是Java比较Flexible一点…

#include<bits/stdc++.h>
using namespace std;
typedef struct node{
    int val;
    int h;
    struct node* left;
    struct node* right;
}avlnode;
typedef avlnode* avlptr;
avlptr init(){
    return (avlptr)malloc(sizeof(avlnode));
}

int getH(avlptr root){
    if(root==nullptr){
        return 0;
    }
    else{
        return max(root->left!=nullptr?root->left->h:0,root->right!=nullptr?root->right->h:0)+1;
    }
}
avlptr rightRotate(avlptr root){
    avlptr left = root->left;
    root->left = left->right;
    left->right = root;

    left->right->h = getH(left->right);
    left->h = getH(left);
    return left;
}
avlptr leftRotate(avlptr root){
    avlptr right = root->right;
    root->right = right->left;
    right->left = root;

    right->left->h = getH(right->left);
    right->h = getH(right);
    return right;
}
int getF(avlptr root){
    return (root->left!=nullptr?root->left->h:0 )- (root->right!=nullptr?root->right->h:0);
}
avlptr rebalance(avlptr root){
    int factor = getF(root);
    //cout<<"bf="<<factor<<endl;
    if(factor>1){
        if(getF(root->left)>0){
            //LL
            return rightRotate(root);
        }else if(getF(root->left)<=0){
            //LR
            root->left = leftRotate(root->left);
            return rightRotate(root);
        }
    }
    else if(factor<-1){
        if(getF(root->right)<0){
            //RR
            return leftRotate(root);
        }
        else if(getF(root->right)>=0){
            //RL
            root->right = rightRotate(root->right);
            return leftRotate(root);
        }
    }
    else{
        return root;
    }
}

avlptr insert(avlptr root,int val){
    if(root==nullptr){
        root = init();
        root->val = val;
        root->h=1;
        root->left = nullptr;
        root->right = nullptr;
        return root;
    }
    else{
        if(root->val==val){
            //有些题目会保证不重复。
        }
        else if(root->val > val){
            root->left = insert(root->left,val);
        }else{
            root->right = insert(root->right,val);
        }
        //root->h = max(root->left!=nullptr?root->left->h:0,root->right!=nullptr?root->right->h:0)+1;
        root->h = getH(root);
        root = rebalance(root);
    }
    return root;
}

// NOTICE : 默认左子树不为空!!
avlptr preSuccessor(avlptr root){
    if(root==nullptr){
        return nullptr;
    }else{
        avlptr left = root->left;
        while(left->right){
            left = left->right;
        }
        return left;
    }
}

// NOTICE : 默认右子树不为空!!
avlptr successor(avlptr root){
    if(root==nullptr){
        return nullptr;
    }else{
        avlptr right = root->right;
        while(right->left){
            right = right->left;
        }
        return right;
    }
}

int getHeight(avlptr root) {
    if(root == NULL) {
        return 0;
    } else {
        return max(getHeight(root->left),getHeight(root->right)) + 1;
    }
}

void del(int val,avlptr* rootptr){
    avlptr root = *rootptr;
    if(root==nullptr){
        return ;
    }
    if(val<root->val){
        del(val,&(root->left));
    }
    else if(val>root->val){
        del(val,&(root->right));
    }
    else{//val==root->val  找到了
        if(root->left==nullptr&&root->right==nullptr){
            //cout<<root->val<<endl;
            *rootptr = nullptr;
            //cout<<(*rootptr)->val<<endl;
            return;
        }
        else if(root->left!=nullptr){
            avlptr ps = preSuccessor(root);
            root->val = ps->val;
            del(ps->val,&(root->left));
        }else{
            avlptr s = successor(root);
            root->val =  s->val;
            del( s->val,&(root->right));
        }
        
    }
    cout<<"huisu:"<<root->val<<endl;
    (*rootptr)->h = getHeight(*rootptr);
    *rootptr = rebalance(*rootptr);
}

void print(){

}

void inorder(avlptr root){
    if(root==nullptr) return;
    else{
        inorder(root->left);
        cout<<root->val<<"("<<getF(root)<<") ";
        inorder(root->right);
    }
    
}
void preorder(avlptr root){
    if(root==nullptr) return;
    cout<<root->val<<" ";
    preorder(root->left);
    
    preorder(root->right);
}

int main(){
    int n;cin>>n;
    avlptr root = nullptr;
    //cout<<root<<endl;
    while(n--){
        int op;
        cin>>op;
        if(op==1){
            int val;
            cin>>val;
            root = insert(root,val);
        }
        if(op==2){
            int val;
            cin>>val;
            del(val,&root);
        }

    }
    inorder(root);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值