手撕二叉平衡树

今天给大家带来的是平衡树的代码实现,如下:

#pragma once 
#include 
#include 
#include 
#include 
#include 
using namespace std;
namespace cc
{
    template
    struct AVLnode
    {
        int _bf = 0;
        pair _val;
        AVLnode* _left;
        AVLnode* _right;
        AVLnode* _parent;
        AVLnode(const pair& val = pair(), AVLnode* left = nullptr, AVLnode* right = nullptr, AVLnode* parent = nullptr)
            : _val(val)
            , _left(left)
            , _right(right)
            , _parent(parent)
        {}
    };
    template
    class AVL
    {
    public:
        typedef AVLnode node;
        //此parent其实就相当于是旋转点
        void revolveL(node* parent)
        {
            //需要改变的节点
            node* sub = parent->_right;
            node* subl = sub->_left;
            //如果根节点是parent
            if (_root == parent)
            {
                _root = sub;
                sub->_parent = nullptr;
                sub->_left = parent;
                parent->_parent = sub;
                parent->_right = subl;
                if (subl)
                    subl->_parent = parent;
            }
            //此旋转点不是根节点
            else
            {
                node* pparent = parent->_parent;
                if (pparent->_left == parent)
                    pparent->_left = sub;
                else
                    pparent->_right = sub;
                sub->_parent = pparent;
                sub->_left = parent;
                parent->_parent = sub;
                parent->_right = subl;
                if (subl)
                    subl->_parent = parent;
            }
            //旋转完成,更新平衡因子
            sub->_bf = 0;
            parent->_bf = 0;
        }
        void revolveR(node* parent)
        {
            node* sub = parent->_left;
            node* subr = sub->_right;
            if (_root == parent)
            {
                _root = sub;
                sub->_parent = nullptr;
                sub->_right = parent;
                parent->_parent = sub;
                parent->_left = subr;
                if (subr)
                    subr->_parent = parent;
            }
            else
            {
                node* pparent = parent->_parent;
                if (pparent->_left == parent)
                    pparent->_left = sub;
                else
                    pparent->_right = sub;
                sub->_parent = pparent;
                sub->_right = parent;
                parent->_parent = sub;
                parent->_left = subr;
                if (subr)
                    subr->_parent = parent;
            }
            sub->_bf = 0;
            parent->_bf = 0;
        }
        bool insert(const pair& x)
        {
            //根节点为空
            if (_root == nullptr)
            {
                _root = new node(x);
                //节点中父指针已经指向nullptr
                return true;
            }
            node* parent = nullptr;
            node* cur = _root;
            while (cur)
            {
                if (x.first < (cur->_val).first)
                {
                    parent = cur;
                    cur = cur->_left;
                }
                else if (x.first > (cur->_val).first)
                {
                    parent = cur;
                    cur = cur->_right;
                }
                else
                    return false;
            }
            cur = new node(x);
            if ((parent->_val).first > x.first)
                parent->_left = cur;
            else
                parent->_right = cur;
            cur->_parent = parent;
            //插入完成,更新平衡因子
            while (parent)
            {
                if (parent->_right == cur)
                    parent->_bf++;
                else if (parent->_left == cur)
                    parent->_bf--;
                //此情况说明cur既不是做孩子也不是右孩子,所以直接报错,说明插入的时候出现了问题
                else
                    assert(false);
                if (abs(parent->_bf) == 0)
                    break;
                else if (abs(parent->_bf) == 1)
                {
                    cur = parent;
                    parent = parent->_parent;
                }
                else if (abs(parent->_bf) == 2)
                {
                    //旋转
                    if (parent->_bf == 2 && cur->_bf == 1)
                    {
                        revolveL(parent);
                        break;
                    }
                    else if (parent->_bf == -2 && cur->_bf == -1)
                    {
                        revolveR(parent);
                        break;
                    }
                    else if (parent->_bf == -2 && cur->_bf == 1)
                    {
                        node* sub = parent->_left;
                        node* subr = sub->_right;
                        int bf = subr->_bf;
                        revolveL(sub);
                        revolveR(parent);
                        if (bf == 0)
                        {
                            parent->_bf = 0;
                            sub->_bf = 0;
                            subr->_bf = 0;
                        }
                        else if (bf == 1)
                        {
                            sub->_bf = -1;
                            parent->_bf = 0;
                            subr->_bf = 0;
                        }
                        else if (bf == -1)
                        {
                            sub->_bf = 0;
                            parent->_bf = 1;
                            subr->_bf = 0;
                        }
                        else
                            assert(false);
                        break;
                    }
                    else if (parent->_bf == 2 && cur->_bf == -1)
                    {
                        node* sub = parent->_right;
                        node* subl = sub->_left;
                        int bf = subl->_bf;
                        revolveR(sub);
                        revolveL(parent);
                        subl->_bf = 0;
                        if (bf == 0)
                        {
                           // subl->_bf = 0;
                            sub->_bf = 0;
                            parent->_bf = 0;
                        }
                        else if (bf == 1)
                        {
                            sub->_bf = 0;
                           // subl->_bf = 0;
                            parent->_bf = -1;
                        }
                        else if (bf == -1)
                        {
                            sub->_bf = 1;
                            //subl->_bf = 0;
                            parent->_bf = 0;
                        }
                        else
                            assert(false);
                        break;
                    }
                    //如果走到这步,说明这棵树的平衡因子有问题
                    else
                        assert(false);
                }
                else
                    assert(false);
            }
            return true;
        }
        int high()
        {
            return _high(_root);
        }
        bool check()
        {
            return _check(_root);
        }
    private:
        node* _root = nullptr;
        bool _check(node* root)
        {
            if (root == nullptr)
                return true;
            int bf = _high(root->_right) - _high(root->_left) ;
            if (bf != root->_bf)
            {
                cout << "bf:不一样" << endl;
                return false;
            }
            cout << "bf:" << bf <<" " << "root:" << (
                root->_val).first << endl;
            return _check(root->_left) && _check(root->_right);
        }
        int _high(node *root)
        {
            if (root == nullptr)
                return 0;
            int left = _high(root->_left);
            int right = _high(root->_right);
            if (left > right)
                return left + 1;
            else
                return right + 1;
        }
    };
}

这个仅仅是平衡树的插入,其实二叉平衡树的插入并不难,逻辑就是那么几个,但是难得是细节处的实现,尤其是平衡因子更新的那一块,特别的容易弄混人,只要记住四种模型就可以了。两种单旋,两种双旋,还是有点难以理解的,下面一一讲解

1.左单旋

左单旋大概的图示这样子的:

手撕二叉平衡树_第1张图片

如上图,如果没有插入100,那么此时的二叉平衡树是平衡的,但是此时如果插入100,此时30这个节点的平衡因子是2,所以此时需要旋转来降低这棵树的高度,此时就是左旋。其实此处还分为好几种情况,但是这几种情况都是一样的旋转方法,因为都在60这个节点的右子树中,所以此时就把30当做旋转点,让60左旋,在把60这个节点的左子树链接到30的右指针处就好了。

2.右单旋

手撕二叉平衡树_第2张图片

和左单旋一样,因为新插入的节点导致30这个节点的平衡因子为-2,所以此时就要旋转来降低这棵树的高度,此时是要右旋,这个和左旋一样,30是旋转点,25进行右旋,把25这个节点的右子树连接到30这个节点的左子树处就可以了。

3.先左旋,在右旋

手撕二叉平衡树_第3张图片

这个就是先左旋,在右旋,也是插入了新的节点导致的。这里没有写节点具体的值是因为,因为40这个节点的右子树或是左子树中的值不确定,所以就用了一个空节点来代替插入的值。其实就是两次单旋的结果,先把40以30为旋转点进行左旋,再把60当旋转点进行右旋,此时就旋转完成。而30与40的子树怎么连接参考左单旋与右单旋的旋转方式

4.先右旋,在左旋

手撕二叉平衡树_第4张图片

这个模型就是先右旋,在左旋。先把60当旋转点进行旋转,再把30当旋转点进行旋转,至于子树怎么连接,与先左旋,在右旋相同。

以上就是平衡二叉树的旋转方式,下面来总结一下思路:

1.与二叉搜索树的插入一样

2.链接parent指针

3.更新平衡因子

4.如果左右不平衡,那么就开始旋转

增删查时间复杂度:

首先我们知道的是,他是一个二叉树,所以他的高度是log n,而我们在增删查的时候,我们最坏的结果就是要查叶子结点或者所查的节点不在此树,此时它的时间复杂度就是log n,但是他的空间复杂度也是logn,所以个人认为他是以空间换区时间的一种数据结构,但是他的效率确实很高,而对于我们所说的红黑树,其实严格意义来说也是一种logn的算法,但是他没有平衡二叉树这么严谨,平衡二叉树旋转的次数比较多,但是红黑树却没有,旋转其实也是一种消耗,但是旋转的时间复杂度是O(1),严格来说,有消耗,但是也没那么严重,但是对于红黑树来说,就是尽可能不旋转,减少这种消耗。

对于红黑树的代码以及讲解,下一篇会详细讲解,也会对比AVL树和红黑树的优缺点。期待下一篇内容吧!!谢谢大家支持!!!

你可能感兴趣的:(C++篇,数据结构,算法)