• 数据结构/C++:红黑树



    概念

    红黑树是一种二叉搜索树,一般的二叉搜索会发送不平衡现象,导致搜索效率下降,于是学者们开始探索如何让二叉搜索树保持平衡,这种树叫做自平衡二叉搜索树。起初学者发明了AVL树,其通过一定算法保持了二叉搜索树的严格平衡,不久后Rudolf Bayer发明了红黑树,红黑树的平衡是较为宽泛的,为了保持平衡,红黑树付出的代价比AVL树更小。因此红黑树被更为广泛的使用,比如Java,C++,python中,使用的自平衡二叉搜索树都是红黑树,而不是AVL树。

    如果想了解AVL树,可以看这篇博客:[数据结构/C++:AVL树]

    红黑树的要求如下:

    红黑树中,最长路径的长度不会超过最短路径的两倍

    先解释一下路径的概念:从根走到nullptr
    有不少人认为路径是从根走到叶子节点,这是不正确的。

    红黑树用了五条规则来限制一棵树,从而达到以上要求:

    1. 每个节点不是红色就是黑色
    2. 根节点一定是黑色
    3. 不可以出现连续的红色节点(黑色可以连续出现)
    4. 每一条路径都包含相同数目的黑色节点
    5. nullptr视为黑色节点

    只要满足以上五个条件,那么这棵树就是一颗红黑树,而且满足最长路径的长度不会超过最短路径的两倍。为什么呢?

    五条规则中,我标红了3,4两条规则:

    1. 不可以出现连续的红色节点(黑色可以连续出现)
    2. 每一条路径都包含相同数目的黑色节点

    由于每一条路径都必须包含相同数目的黑色节点,现在我们假设一棵红黑树,所有路径的黑色节点数目都是x,那么最短的路径长度就是全为黑色节点,长度为x
    如果想让一条路径变长,那么就只能插入更多的红色节点(因为黑色节点数目相同),但是红色节点又不能连续出现,所以只能是黑红黑红黑红黑红黑红......这样排列,一个黑节点匹配一个红节点,因此最长路径的长度就是黑色节点的两倍2x
    可以发现,红黑树通过这两条核心规则,保证了二叉搜索树的平衡。

    比如以下就是一颗红黑树:
    在这里插入图片描述

    其最短路径为最左侧的路径,长度为2,即两个黑节点。
    其最长路径为最右侧的路径,长度为4,即一红一黑排列。

    要注意的是:不是所有的红黑树都会出现以上的全黑路径,或者一红一黑路径的,这只是极端情况

    接下来我们通过实现红黑树,来了解红黑树是如何自平衡的:


    实现

    基本结构

    首先我们要在节点中加入一个成员来表示节点的颜色,颜色有红黑和黑色两种状态,这里我使用枚举来区分两者:

    enum Colour
    {
        RED,
        BLACK
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在某些红黑树的实现中,使用bool值来表示红黑颜色,这也是可以的,但是本博客以枚举来表示颜色。

    节点类:

    template<class K, class V>
    struct RBTreeNode
    {
        RBTreeNode* _left;
        RBTreeNode* _right;
        RBTreeNode* _parent;
        pair<K, V> _kv;
        Colour _col;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    _left:左子树
    _right:右子树
    _parent:父节点
    _kv:节点存储的值
    _col:该节点的颜色

    节点类还需要一个构造函数进行初始化,现在的问题就是:新的节点要初始化为什么颜色?
    先来考虑一下:插入红色节点和插入黑色节点,谁对红黑树影响大?
    对于一棵红黑树,其所有路径的黑色节点数目都相同,如果我们在某一条路径末尾插入了黑色节点,那么整棵树的所有其它路径都会少一个黑节点。而插入红色节点只影响当前路径,所以新节点应该是红色节点

    构造函数:

    RBTreeNode(const pair<K, V>& kv)
        : _left(nullptr)
        , _right(nullptr)
        , _parent(nullptr)
        , _kv(kv)
        , _col(RED)//初始化为红节点
    {}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    接着就是红黑树本体,类中只存储一个根节点_root

    template<class K, class V>
    class RBTree
    {
        typedef RBTreeNode<K, V> Node;
    private:
    	Node* _root = nullptr;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    现在我们有了红黑树的基本结构,接下来就实现它的插入操作:


    插入

    那么我们先写出当基本的二叉搜索树的插入代码逻辑,既然要插入,那么就要先找到合适的位置插入,代码如下:

    bool Insert(const pair<K, V>& kv)
    {
        if (_root == nullptr)
        {
            _root = new Node(kv);
            _root->_col = BLACK;//保持根为黑节点
        }
    
        Node* cur = _root;
        Node* parent = nullptr;
    
        while (cur)
        {
            if (cur->_kv.first < kv.first)
            {
                parent = cur;
                cur = cur->_right;
            }
            else if (cur->_kv.first > kv.first)
            {
                parent = cur;
                cur = cur->_left;
            }
            else
            {
                return false;
            }
        }
    
        cur = new Node(kv);
    
        if (parent->_kv.first > kv.first)
            parent->_left = cur;
        else
            parent->_right = cur;
    
        cur->_parent = parent;
       
       //调整红黑树
       //......
       //......
       //......
       
        return true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    接下来,我先解析以上代码的逻辑:

    if (_root == nullptr)
    {
    	_root = new Node(kv);
        _root->_col = BLACK;//保持根为黑节点
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果我们插入节点时,根节点_root为空,说明当前整棵树都为空,那么我们直接插入值作为根节点即可,但是根节点必须是黑色节点,而我们新插入的节点是红色,所以要将其调整为黑色节点。


    while (cur)
    {
    	if (cur->_kv.first < kv.first)
    	{
    		parent = cur;
    		cur = cur->_right;
    	}
    	else if (cur->_kv.first > kv.first)
    	{
    		parent = cur;
    		cur = cur->_left;
    	}
    	else
    	{
    		return false;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    以上代码,是在找到合适的插入位置,当key大于当前节点cur->_kv.first < kv.first,那么cur就向左寻找,反之向右寻找。如果当前节点值等于key,那么说明该节点已经存在,返回false代表插入失败。当我们的cur为空指针,说明已经找到了插入的节点,此时跳出循环进行插入。


    cur = new Node(kv);
    
    if (parent->_kv.first > kv.first)
    	parent->_left = cur;
    else
    	parent->_right = cur;
    
    cur->_parent = parent;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    到达此处,说明前面已经找到插入的位置了,而parent节点就是插入位置的父亲节点。根据key的大小,来判断插入到左边还是右边,插入完成后,再让新节点的_parent指向parent

    至此我们就完成了插入操作,接下来就要根据不同情况对红黑树进行调整。


    对于红黑树的插入,我们需要关注新节点的父亲parent,祖父grandfather,叔叔uncle三个节点:
    在这里插入图片描述

    1. 先根据父亲节点的颜色,来判断是否需要调整

    父亲节点为黑色:
    在这里插入图片描述
    新插入的节点默认为红色,所以新插入节点不会影响路径上黑色节点的数目,而parent是黑节点,我们也没有出现连续的红色节点,所以这种情况无需任何调整,直接插入就可以。

    父亲节点为红色:
    在这里插入图片描述
    如果父亲节点为红色,我们就会出现连续的红色节点,这时我们就需要进行调整了

    以上两种情况总结为:

    parent为黑色,直接插入
    parent为红色,插入后需要进行调整

    当前的代码为:

    在这里插入代码片
    
    • 1

    parent为红色,我们就需要再根据uncle的颜色,将插入分类两类:uncle为红色以及uncle为黑色
    在这里插入图片描述
    值得注意的是:由于parent是红色节点,此时的grandfather一定是黑色节点,因为不能出现连续红色节点
    这两种情况的操作不同,我们先看到uncle为红色的情况:


    uncle为红色节点

    uncle节点为红色,此时需要进行变色

    变色如下:
    在这里插入图片描述

    由于新插入了红色的cur节点,此时parentcur出现了连续的红色节点,于是我们将parent改为黑色。但是此时以parent为根的所有路径就会多出一个黑节点,于是把grandfather变为红色,来抵消这个新增的黑节点。但是此时以uncle为根的路径又会少一个黑节点,于是把uncle变黑。

    但是我们grandfather变为了红色,这有可能会影响到上一层节点,比如这样:
    在这里插入图片描述
    我们把grandfather变红之后,又出现了两个红色节点相连的情况,所以我们要写一个while循环,来反复向上检查。

    当前代码如下:

    while (parent && parent->_col == RED)//只有parent为红,才更新 (parent可能不存在)
    {
        Node* grandfather = parent->_parent;
        if (parent == grandfather->_left)
        {
            Node* uncle = grandfather->_right;
    
            //uncle存在且为红节点
            if (uncle && uncle->_col == RED)
            {
                parent->_col = uncle->_col = BLACK;
                grandfather->_col = RED;
    
                cur = grandfather;
                parent = cur->_parent;
            }
            else//uncle为黑节点 
            {
                //其它处理
            }
        }
        else
        {
            Node* uncle = grandfather->_left;
    
            //uncle存在且为红节点
            if (uncle && uncle->_col == RED)
            {
                parent->_col = uncle->_col = BLACK;
                grandfather->_col = RED;
    
                cur = grandfather;
                parent = cur->_parent;
            }
            else//uncle为黑节点 
            {
            	//其它处理
            }
        }
    }
    
    _root->_col = BLACK;//在循环内部不判断root情况,统一处理
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    代码解析:

    while (parent && parent->_col == RED)
    
    • 1

    该代码用于检测curparent的颜色,通过我们前面的推导,如果parent为红色才需要调整,因此进入循环的条件之一是parent为红色。另外的parent有可能为nullptr,此时我们要避免访问空指针,所以空指针也不能进循环


    if (parent == grandfather->_left)
    {  }
    else
    { }
    
    • 1
    • 2
    • 3
    • 4

    这一段代码是在检测parent 节点是grandfather的左子树还是右子树,这将涉及到我们如何找uncle以及下一种情况的调整,此时我们要分类讨论。当parent == grandfather->_left成立,那么uncle就是grandfather的右子树:Node* uncle = grandfather->_right;,反之就是左子树


    if (uncle && uncle->_col == RED)
    {
    	parent->_col = uncle->_col = BLACK;
    	grandfather->_col = RED;
    
    	cur = grandfather;
    	parent = cur->_parent;
    }      
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    我们找到uncle后,如果uncle是红色,那么直接进行变色操作,把parentuncle的颜色变为黑色,grandfather变为红色。
    随后由于我们的变色操作可能会影响上一层,此时调整节点,进入下一次while循环


    在整个while循环外侧,还有一句代码:

    _root->_col = BLACK;
    
    • 1

    这是因为我们在先前的while循环中,有可能出现对_root节点的操作,导致_root的颜色改变,而_root需要保持黑色。如果我们在循环内部,每一次都检测_root有点麻烦了,于是我们直接在每一次调整完节点后,把_root强行矫正为黑色

    至此我们就讨论完了uncle为红色节点的情况,接下来我们就讨论uncle为黑色节点:


    uncle为黑色节点

    由于红黑树中,nullptr也算作黑色节点,所以uncle为黑色分为以下两种情况:

    1. uncle为空指针
    2. uncle不为空指针

    图示如下:
    在这里插入图片描述

    如果 uncle为空指针,那么cur一定是新插入的节点

    因为如果cur不是新插入的节点,那么curparent一定有一个原先是黑色节点,不然会出现连续的红色节点。但是如果curparent有一个是黑色节点,那么grandfather的左子树就比右子树多出一个黑节点,这就违背了红黑树规则。无论怎样,原先的树都不可能符合规则,所以cur一定是新插入的节点,破坏了规则

    如果 uncle不为空指针,那么cur一定是从黑色节点变成的红色节点(不是新插入的)

    因为如果uncle存在,那么grandfather的右子树就存在一个黑节点,而parent是红节点,所以curparent的右子树中都至少有一个黑节点,才能保证每一条路径黑节点数目相同。因此cur原先一定是黑节点,是因为cur下层插入了新节点,然后通过while循环向上走,影响到了当前层

    对于这种uncle为黑色的情况,我们需要通过旋转+变色来维持红黑树。

    旋转又分为单旋和双旋:

    curparent的关系和parentgrandfather的关系一致时,需要进行单旋

    比如我们刚刚的情况:
    在这里插入图片描述
    curparent的左子树,parentgrandfather的左子树,关系一致。
    我们需要对其进行右单旋+变色:
    在这里插入图片描述
    这个旋转的算法在此我就不过多讲解了,可以去AVL树的博客中了解。我重点讲解一下变色和旋转的合理性:

    一次插入过程中,走到这一步,说明前面一定经过了uncle为红色的情况,而uncle为红色的情况进行变色并不会对任何路径的黑色节点数目造成影响,因此目前还是符合黑色节点数目相同规则的
    同为parent的子树,以curC为根的路径,黑节点数目相同
    同为grandfather的子树,以parentuncle为根的路径黑节点数目相同
    parent是红色节点,所以curC以及uncle为根的路径,黑节点数目都相同


    进行单旋,会把c树交给grandfather做子树,而cuncle为根的路径黑节点数目相同,不违背规则(旋转的合理性)


    旋转后,parent作新根,grandfathercur作为左右子树grandfather为根的路径,整体上就会比以cur为根的路径多出一个黑节点(即grandfather本身)
    因此,将grandfather改为红节点,来平衡parent左右子树的黑节点
    而红色节点不能连续出现,再把parent改为黑节点

    curparent的关系和parentgrandfather的关系不一致时,需要进行双旋

    在这里插入图片描述
    以上结构中,curparent的左子树,parentgrandfather的右子树,关系不一致,要进行双旋。
    同样的,讲解一下变色和旋转的合理性:

    一次插入过程中,走到这一步,说明前面一定经过了uncle为红色的情况,而uncle为红色的情况进行变色并不会对任何路径的黑色节点数目造成影响,因此目前还是符合黑色节点数目相同规则的
    同为parent的子树,以curA为根的路径,黑节点数目相同
    同为cur的子树,以BC为根的路径,黑节点数目相同
    由于cur是红节点,所以以ABC为根的路径,黑节点数目相同
    相同的手段,由于parent是红节点,所以Auncle为根的路径的黑节点数目相同
    因此ABCuncle为根的路径,黑节点数目都相同


    进行双旋,会把C子树交给grandfather做子树,而Cuncle黑节点数目相同,不违背规则也会把B交给parent做子树
    AB黑节点数目相同,不违背规则
    旋转后,cur作新根,grandfatherparent作为左右子树grandfather为根的路径,整体上就会比以parent为根的路径多出一个黑节点(grandfather本身)
    因此,将grandfather改为红节点,来平衡cur左右子树的黑节点而红色节点不能连续出现,再把cur改为黑节点

    以上单旋和双旋的变色,看似复杂,其实最后都是把新根的颜色变为黑色,新根的左右子树变为红色。由于我们旋转后,新根都是黑节点,所以不会影响上层,可以直接跳出循环

    代码如下:

    parent == grandfather->_left

    else//uncle为黑节点 (旋转)
    {
        if (cur == parent->_left)
        {
            RotateR(grandfather);//右单旋
            parent->_col = BLACK;//变色
            grandfather->_col = RED;//变色
        }
        else
        {
            RotateL(parent);//左右双旋 - 左单旋
            RotateR(grandfather);//左右双旋 - 右单旋
    
            cur->_col = BLACK;//变色
            grandfather->_col = RED;//变色
        }
    
        break;//旋转后一定平衡
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    parent == grandfather->_right

    else//uncle为黑节点 (旋转)
    {
        if (cur == parent->_right)
        {
            RotateL(grandfather);//左单旋
            parent->_col = BLACK;//变色
            grandfather->_col = RED;//变色
        }
        else
        {
            RotateR(parent);//右左双旋 - 右单旋
            RotateL(grandfather);//右左双旋 - 左单旋
    
            cur->_col = BLACK;//变色
            grandfather->_col = RED;//变色
        }
    
        break;//旋转后一定平衡
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    insert总代码:

    bool Insert(const pair<K, V>& kv)
    {
        if (_root == nullptr)
        {
            _root = new Node(kv);
            _root->_col = BLACK;//保持根为黑节点
        }
    
        Node* cur = _root;
        Node* parent = nullptr;
    
        while (cur)
        {
            if (cur->_kv.first < kv.first)
            {
                parent = cur;
                cur = cur->_right;
            }
            else if (cur->_kv.first > kv.first)
            {
                parent = cur;
                cur = cur->_left;
            }
            else
            {
                return false;
            }
        }
    
        cur = new Node(kv);
    
        if (parent->_kv.first > kv.first)
            parent->_left = cur;
        else
            parent->_right = cur;
    
        cur->_parent = parent;
    
        while (parent && parent->_col == RED)//只有parent为红,才更新 (parent可能不存在)
        {
            Node* grandfather = parent->_parent;
            if (parent == grandfather->_left)
            {
                Node* uncle = grandfather->_right;
    
                //uncle存在且为红节点
                if (uncle && uncle->_col == RED)
                {
                    parent->_col = uncle->_col = BLACK;
                    grandfather->_col = RED;
    
                    cur = grandfather;
                    parent = cur->_parent;
                }
                else//uncle不存在或为黑节点 (旋转)
                {
                    if (cur == parent->_left)
                    {
                        RotateR(grandfather);
                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else
                    {
                        RotateL(parent);
                        RotateR(grandfather);
    
    
                        cur->_col = BLACK;
                        grandfather->_col = RED;
                    }
    
                    break;//旋转后一定平衡
                }
            }
            else
            {
                Node* uncle = grandfather->_left;
    
                //uncle存在且为红节点
                if (uncle && uncle->_col == RED)
                {
                    parent->_col = uncle->_col = BLACK;
                    grandfather->_col = RED;
    
                    cur = grandfather;
                    parent = cur->_parent;
                }
                else//uncle不存在或为黑节点 (旋转)
                {
                    if (cur == parent->_right)
                    {
                        RotateL(grandfather);
                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else
                    {
                        RotateR(parent);
                        RotateL(grandfather);
    
                        cur->_col = BLACK;
                        grandfather->_col = RED;
                    }
    
                    break;//旋转后一定平衡
                }
            }
        }
    
        _root->_col = BLACK;//在循环内部不判断root情况,统一处理
    
        return true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114

    总代码展示

    红黑树总代码:
    RBTree.h

    #pragma once
    #include 
    #include 
    using namespace std;
    
    enum Colour
    {
        RED,
        BLACK
    };
    
    template<class K, class V>
    struct RBTreeNode
    {
        RBTreeNode* _left;
        RBTreeNode* _right;
        RBTreeNode* _parent;
        pair<K, V> _kv;
        Colour _col;
    
        RBTreeNode(const pair<K, V>& kv)
            : _left(nullptr)
            , _right(nullptr)
            , _parent(nullptr)
            , _kv(kv)
            , _col(RED)
        {}
    };
    
    template<class K, class V>
    class RBTree
    {
        typedef RBTreeNode<K, V> Node;
    public:
        bool Insert(const pair<K, V>& kv)
        {
            if (_root == nullptr)
            {
                _root = new Node(kv);
                _root->_col = BLACK;//保持根为黑节点
            }
    
            Node* cur = _root;
            Node* parent = nullptr;
    
            while (cur)
            {
                if (cur->_kv.first < kv.first)
                {
                    parent = cur;
                    cur = cur->_right;
                }
                else if (cur->_kv.first > kv.first)
                {
                    parent = cur;
                    cur = cur->_left;
                }
                else
                {
                    return false;
                }
            }
    
            cur = new Node(kv);
    
            if (parent->_kv.first > kv.first)
                parent->_left = cur;
            else
                parent->_right = cur;
    
            cur->_parent = parent;
    
            while (parent && parent->_col == RED)//只有parent为红,才更新 (parent可能不存在)
            {
                Node* grandfather = parent->_parent;
                if (parent == grandfather->_left)
                {
                    Node* uncle = grandfather->_right;
    
                    //uncle存在且为红节点
                    if (uncle && uncle->_col == RED)
                    {
                        parent->_col = uncle->_col = BLACK;
                        grandfather->_col = RED;
    
                        cur = grandfather;
                        parent = cur->_parent;
                    }
                    else//uncle不存在或为黑节点 (旋转)
                    {
                        if (cur == parent->_left)
                        {
                            RotateR(grandfather);
                            parent->_col = BLACK;
                            grandfather->_col = RED;
                        }
                        else
                        {
                            RotateL(parent);
                            RotateR(grandfather);
    
                            cur->_col = BLACK;
                            grandfather->_col = RED;
                        }
    
                        break;//旋转后一定平衡
                    }
                }
                else
                {
                    Node* uncle = grandfather->_left;
    
                    //uncle存在且为红节点
                    if (uncle && uncle->_col == RED)
                    {
                        parent->_col = uncle->_col = BLACK;
                        grandfather->_col = RED;
    
                        cur = grandfather;
                        parent = cur->_parent;
                    }
                    else//uncle不存在或为黑节点 (旋转)
                    {
                        if (cur == parent->_right)
                        {
                            RotateL(grandfather);
                            parent->_col = BLACK;
                            grandfather->_col = RED;
                        }
                        else
                        {
                            RotateR(parent);
                            RotateL(grandfather);
    
                            cur->_col = BLACK;
                            grandfather->_col = RED;
                        }
    
                        break;//旋转后一定平衡
                    }
                }
            }
    
            _root->_col = BLACK;//在循环内部不判断root情况,统一处理
    
            return true;
        }
        
        //左单旋
        void RotateL(Node* parent)
        {
            Node* subR = parent->_right;
            Node* subRL = subR->_left;
    
            parent->_right = subRL;
            if (subRL)
                subRL->_parent = parent;
    
            subR->_left = parent;
            Node* ppNode = parent->_parent;
            parent->_parent = subR;
    
            if (parent == _root)
            {
                _root = subR;
                subR->_parent = nullptr;
            }
            else
            {
                if (ppNode->_left == parent)
                    ppNode->_left = subR;
                else
                    ppNode->_right = subR;
    
                subR->_parent = ppNode;
            }
        }
    
        //右单旋
        void RotateR(Node* parent)
        {
            Node* subL = parent->_left;
            Node* subLR = subL->_right;
    
            parent->_left = subLR;
            if (subLR)
                subLR->_parent = parent;
    
            subL->_right = parent;
            Node* ppNode = parent->_parent;
            parent->_parent = subL;
    
            if (parent == _root)
            {
                _root = subL;
                subL->_parent = nullptr;
            }
            else
            {
                if (ppNode->_left == parent)
                    ppNode->_left = subL;
                else
                    ppNode->_right = subL;
    
                subL->_parent = ppNode;
            }
        }
    
        size_t Size()
        {
            return _Size(_root);
        }
    
        size_t _Size(Node* root)
        {
            if (root == nullptr)
                return 0;;
    
            return _Size(root->_left) + _Size(root->_right) + 1;
        }
    
        Node* Find(const K& key)
        {
            Node* cur = _root;
    
            while (cur)
            {
                if (cur->_kv.first < key)
                {
                    cur = cur->_right;
                }
                else if (cur->_kv.first > key)
                {
                    cur = cur->_left;
                }
                else
                {
                    return cur;
                }
            }
    
            return nullptr;
        }
    
        //中序
        void InOrder()
        {
            _InOrder(_root);
            cout << "end" << endl;
        }
    
        int Height()
        {
            return _Height(_root);
        }
    
    private:
        //中序
        void _InOrder(Node* root)
        {
            if (root == nullptr)
                return;
    
            _InOrder(root->_left);
            cout << root->_kv.first << " - ";
    
            _InOrder(root->_right);
        }
    
        //求高度
        int _Height(Node* root)
        {
            if (root == nullptr)
                return 0;
    
            return max(Height(root->_left), Height(root->_right)) + 1;
        }
    
        Node* _root = nullptr;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280

  • 相关阅读:
    【Spring boot 导入 spring XML 配置文件】
    SpringBoot携带Jre绿色部署项目_免安装Jdk[Linux服务器]
    翌加科技:新手自学短视频简介不可忽视的剪辑攻略
    langchain callback学习
    GBase8s数据库SET CONSTRAINTS 语句
    在推荐系统中,BPRloss、Embloss、CrossEntropyloss是怎么计算的,代表的意义是什么
    INI 文件 - 文件格式规范
    Java 中的异常处理机制
    SVN服务器迁移-Windows
    安装docker,docker-compose
  • 原文地址:https://blog.csdn.net/fsdfafsdsd/article/details/136465711