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

一、概念及性质
AVL树又称为高度平衡的二叉搜索树,是一个“加上了额外平衡条件的二叉搜索树”所以插入的规则是按照二叉搜索树来的。
AVL数具有以下性质:
1. 左子树和右子树的高度之差的绝对值不超过1
2. 树中的每个左子树和右子树都是AVL树
3. 每个节点都有一个平衡因子(balance factor–bf),任一节点的平衡因子是-1,0,1。(每个节点的平衡因子等于右子树的高度减去左子树的高度 )

二、插入操作
插入操作实现和搜索二叉树的插入操作是相同的,需要注意的是,在插入完成后需要进行场景的分类,以便进行对应的旋转以及调节平衡因子操作。对于调节的过程才是关键!
具体实现代码如下:
    bool Insert(const K& key, const V& value)
    {
        if (_root == NULL)
        {
            _root = new Node(key, value);
            return true;
        }
        Node* parent = NULL;
        Node* cur = _root;
        while (cur)
        {
            if (key < cur->_key)
            {
                parent = cur;
                cur = cur->_left;
            }
            else if (key > cur->_key)
            {
                parent = cur;
                cur = cur->_right;
            }
            else
                return false;
        }
        cur = new Node(key, value);
        if (key < parent->_key)
            parent->_left = cur;
        else
            parent->_right = cur;
        cur->_parent = parent;

        //调节平衡
        while (parent)
        {
            if (cur == parent->_left)
                parent->_bf -= 1;
            else
                parent->_bf += 1;

            if (parent->_bf == 0)
                return true;
            else if (parent->_bf == -1
                || parent->_bf == 1)
            {
                cur = parent;
                parent = cur->_parent;
            }
            else if (parent->_bf == -2
                || parent->_bf == 2)
            {
                if (parent->_bf == 2)
                {
                    if (cur->_bf == 1)
                        RotateL(parent);
                    else
                        RotateRL(parent);
                }
                else
                {
                    if (cur->_bf == -1)
                        RotateR(parent);
                    else
                        RotateLR(parent);
                }
            }
            else
            {
                assert(false);
            }
        }
        return true;
    }

三、旋转调节平衡
由于AVL树是一个平衡搜索二叉树,具有自己的特性。那么就肯定会出现不符合AVL树的场景,此时就需要进行旋转以及调节平衡因子操作,由于只有“插入节点至根节点”路径上的各节点可能改变平衡状态,所以调账其中深度最深的节点便可使整个树重新平衡,从而保证整个AVL树的性质。对于破坏AVL树性质需要旋转调节,可具体分为以下几种旋转方式:

注:以下我们可将破坏平衡最深节点定为X,也就是进行操作的parent节点
<1>右旋转:右旋转对应情况为插入节点为于X的左子节点的左子树。我们可以想象subL本来在parent节点的右侧,通过向右旋的操作,成为了新的parent节点,所以为右旋转。

右旋中分为以下两种情况:
①subLR和parent->right都为NULL:

这里写图片描述


②subLR和parent->right都不为NULL:

这里写图片描述


在右旋中需要注意以下几点:
1:注意parent->_parent,若存在则需要再判断 parent是祖父节点的左孩子还是右孩子,再对应连接到subL上,若不存在则此时parent为根节点,则需要将subL->_parent置NULL,让subL成为根节点;
2:注意subLR节点,若为NULL,则不能让subLR->parent还指向parent节点,但是parent指向subLR不影响;
3:在旋转调节完成后需要将subL和parent的平衡因子置为0。

具体实现如下:
     //parent->_left右旋到parent的位置
    void RotateR(Node* parent)
    {
        Node* SubL = parent->_left;
        Node* SubLR = SubL->_right;

        parent->_left = SubLR;
        if (SubLR != NULL)
            SubLR->_parent = parent;

        //记录祖父节点以便一会判断
        Node* Pparent = parent->_parent;

        SubL->_right = parent;
        parent->_parent = SubL;

        if (Pparent == NULL)
            _root = SubL;
        else if (Pparent->_left == parent)
            Pparent->_left = SubL;
        else
            Pparent->_right = SubL;

        SubL->_parent = parent;

        //调节平衡因子
        parent->_bf = SubL->_bf = 0;
    }

<2>左旋转:左旋转对应情况为插入节点为于X的右子节点的右子树,与右旋情况十分类似。

左旋中同样分为以下两种情况:
①subRL和parent->left同时为NULL:

这里写图片描述


②subRL和parent->left都不为NULL:

这里写图片描述

所需要注意的和左旋是一样的,实现如下:
    //parent->_right左旋转到parent的位置
    void RotateL(Node* parent)
    {
        //若subrl不为空,则parent->left必不为空

        Node* SubR = parent->_right;
        Node* SubRL = SubR->_left;

        parent->_right = SubRL;
        if (SubRL != NULL)
            SubRL->_parent = parent;

        //记录祖父节点
        Node* Pparent = parent->_parent;

        SubR->_left = parent;
        parent->_parent = SubR;

        //判断祖父节点
        if (Pparent == NULL)
        {
            _root = SubR;
            SubR->_parent = NULL;
        }
        else if (Pparent->_left == parent)
            Pparent->_left = SubR;
        else
            Pparent->_right = SubR;

        SubR->_parent = Pparent;

        //调节平衡因子
        SubR->_bf = parent->_bf = 0;
    }

<3>右左双旋:右左双旋适用场景为插入节点位于X的右子节点的左子树。

第一种情况:subRL->_bf == 0;

这里写图片描述


第二种情况:subRL->_bf == 1;

这里写图片描述


第三种情况:subRL->_bf == -1;

这里写图片描述

左右旋转和右左旋转其实就是对两个节点分别进行左单旋和右单旋,记录好平衡因子以便旋转完成后调节。实现代码如下:
void RotateRL(Node* parent)
    {
        Node* SubR = parent->_right;
        Node* SubRL = SubR->_left;
        int bf = SubRL->_bf;

        RotateR(SubR);
        RotateL(parent);
        if (bf == 1)
        {
            parent->_bf = -1;
            SubR->_bf = SubRL->_bf = 0;
        }
        else if (bf == 1)
        {
            SubR->_bf = 1;
            parent->_bf = SubRL->_bf = 0;
        }
        else if (bf == 0)
            SubRL->_bf = SubR->_bf = parent->_bf = 0;
        else
            assert(false);
    }

<4>左右旋转:
左右旋转场景为:插入点位于X的左子节点的右子树。有了上面的基础,左右旋转也就很容易能分析出来,在此只列举出来对应的三种情况和实现代码。

第一种情况:subRL->_bf == 0;

这里写图片描述


第二种情况:subRL->_bf == 1;

这里写图片描述


第三种情况:subRL->_bf == -1;

这里写图片描述

实现如下:
    void RotateLR(Node* parent)
    {
        Node* SubL = parent->_left;
        Node* SubLR = SubL->_right;
        int bf = SubLR->_bf;

        RotateL(SubLR);
        RotateR(parent);

        if (bf == 1)
        {
            parent->_bf = SubLR->_bf = 0;
            SubL->_bf = -1;
        }
        else if (bf == -1)
        {
            parent->_bf = 1;
            SubLR->_bf = SubL->_bf = 0;
        }
        else if (bf == 0)
            parent->_bf = SubL->_bf = SubLR->_bf = 0;
        else
            assert(false);
    }

简单AVL树实现代码请点击https://github.com/SssUuuu/Data_structure/blob/master/AVLTree
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值