数据结构|平衡二叉搜索树(AVL树)

如果二叉搜索树遇到了极端情况,也就是数组本身就有序,做成BST树就会退化成一个链表,查询时间大大增加。因此需要优化,让我们制作BST树时,能让数值均匀的分布在二叉树的左右。在这里插入图片描述
为了让二叉树平衡,引进了一个平衡因子balance,表示右子树与左子树的高度差,这个平衡因子只有-1,0,1三种取值,-1表示左边比右边高一,0表示平衡,1同理。当balance取其他值时,这个二叉树就不平衡了,因此要做出调整。
调整的方法为左右旋转以及双旋转,下面将逐一讲解。
先写出定义

typedef int ElemType;
typedef struct AVLNode //BinarySortTreeNode
{
    struct AVLNode* leftchild;
    struct AVLNode* parent;
    struct AVLNode* rightchild;
    int balance;  // 0 // 1 -1
    ElemType data;
}AVLNode, * PAVLNode;

typedef struct
{
    AVLNode* root;
    int cursize;
}AVLTree;

购买节点

AVLNode* Buynode(ElemType val)
{
    AVLNode* s = (AVLNode*)malloc(sizeof(AVLNode));
    if (NULL == s) exit(EXIT_FAILURE);
    s->data = val;
    s->leftchild = NULL;
    s->parent = NULL;
    s->rightchild = NULL;
    s->balance=0;
    return s;
}

例如下图中,12节点的balance为2,二叉树不平衡了,如果想让这个树平衡,同时还满足二叉搜索树,也就是中序遍历仍然有序,需要将23作为后根节点,12,34分别作为左右子树。
也就是不平衡的树是一条斜线。
这种旋转称为左旋转。
在这里插入图片描述在这里插入图片描述
逻辑很简单,就是指针的变换
1.将23作为新的根节点newroot
2.23若原有左孩子,就将其赋给12的右孩子(一定大于12)
3.23的左孩子leftchild指向12
4.如果12是根节点,那么23就替换成为根节点
5.如果12不是根节点,那么判断12是左孩子还是右孩子,然后将23替换。
6.双亲节点补全。

代码如下:

void RotateLeft(AVLTree* ptree,AVLNode* ptr){//左旋转
    assert(ptree != NULL && ptr!=NULL);
    AVLNode* newroot=ptr->rightchild;//定义新根节点
    newroot->parent = ptr->parent;//新根节点双亲
    ptr->rightchild = newroot->leftchild;//变换节点的子节点
    if(newroot->leftchild!=NULL){
        newroot->leftchild->parent = ptr;
    }//如果新根节点没有子节点,那么直接改变
    newroot->leftchild = ptr;
    if(ptree->root == ptr){
        ptree->root = newroot;//12若为根节点,newroot直接做根节点
    }else{
        if(ptr->parent->leftchild == ptr){
            ptr->parent->leftchild == newroot;
        }else{
            ptr->parent->rightchild =newroot;
        }
    }//判断12是左子树还是右子树,然后变换。

    ptr->parent = newroot;//3
}

右旋转同理,全部镜像改变

void RotateRight(AVLTree* ptree,AVLNode* ptr){
    assert(ptree!=NULL && ptr!= NULL);
    AVLNode* newroot = ptr->leftchild;
    newroot->parent = ptr->parent;
    ptr->leftchild = newroot->rightchild;
    if(newroot->rightchild != NULL){
        newroot->rightchild->parent =ptr;
    }
    newroot->rightchild = ptr;
    if(NULL == ptr->parent){
        ptree->root = newroot;
    }else{
        if(ptr->parent->leftchild == ptr){
            ptr->parent->leftchild = newroot;
        }else{
            ptr->parent->rightchild=newroot;
        }
    }
    ptr->parent = newroot;
}

当不平衡的区域是一条斜线是,用左右旋转解决不了,就要用到双旋转。
如下:

在这里插入图片描述
原理就是先用一次单旋转,将区域转化成斜线,然后再用一次旋转使之平衡

void LeftBalance(AVLTree* ptree, AVLNode* ptr)
{
    assert(ptree != NULL && ptr != NULL);
    AVLNode* leftsub = ptr->leftchild, * rightsub = NULL;
    switch (leftsub->balance) // 0 1 -1;
    {
        case 0: printf("left balance \n"); break;
        case -1://表示左大于右
            ptr->balance = 0;
            leftsub->balance = 0;
            RotateRight(ptree, ptr);
            break;
        case 1://表示右大于左
            rightsub = leftsub->rightchild;
            switch (rightsub->balance)
            {
                case 0:
                    break;
                case 1:
                    ptr->balance = 0;
                    leftsub->balance = -1;
                    break;
                case -1:
                    ptr->balance = 1;
                    leftsub->balance = 0;
                    break;
            }
            rightsub->balance = 0;
            RotateLeft(ptree,ptr->leftchild);
            RotateRight(ptree,ptr);
            break;
    }
}

右旋转同理,镜像转换。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值