红黑树插入源码解析

概述

红黑树是关联式容器setmapmultisetmultimap的底层实现。

二叉搜索树

BST(Binary Search Tree) 插入、删除、查找时间复杂度都是 **O(log n)** ,但树不平衡的话其性能会变差,最差变为O(n).

二叉搜索树的节点放置规则是:**任何节点的键值一定大于其左子树中的每一个节点的键值、并小于其右子树中的每一个节点的键值。**因此,从根节点一直往左走,直至无左路可走,即得最小元素:从根节点一直往在走,直全无右路可走,即得最大元素。

二叉搜索树的插入删除

插入操作:

插人新元素时,可以根节点开始,遇键值较大者就向左,遇键值较小者就向右,一直到尾端,即为插人点。
在这里插入图片描述

删除操作:

分两种情况:如果 A只有一个子节点,我们就直接将 A的子节点连至A的父节点,并将A删除。如果A 有两个子节点,我们就以有子树内的最小节点取代A。注意,右子树的最小节点极易获得:从右子节点开始 (视为右子树的根节点),一直向左走至底即是。
在这里插入图片描述

平衡二叉树

AVL树具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。在平衡二叉搜索树中,我们可以看到,其高度一般都良好地维持在O(log2n),其各操作的时间复杂度(O(log2n))同时也由此而决定,大大降低了操作的时间复杂度。另外,最小二叉平衡树的节点的公式如下 F(n)=F(n-1)+F(n-2)+1 这个类似于一个递归的数列。

插入操作:

插人新元素时,可以根节点开始,遇键值较大者就向左,遇键值较小者就向右,一直到尾端,即为插人点。

当插入一个节点导致二叉树的平衡状态被打破,就需要调整“插入点到根节点”路径上 平衡被破坏的各节点最深的节点,便可使整棵树重新获得平衡。假设该最深节点为X,由于节点最多拥有两个字节点,而所谓“平衡被破坏”意味着X的左右两棵子树的高度相差2,情况大概分为四种:

  1. 插人点位于叉的左子节点的左子树——左左。
  2. 插人点位于×的左子节点的右子树——左右。
  3. 插人点位于又的右子节点的左子树——右左。
  4. 插人点位于 X的右子节点的右子树—— 右右。

情况1、4彼此对称,称为外侧插入,可以采用单旋转操作调整解决。

在这里插入图片描述

情况2、3彼此对称,称为内侧插入,可以采用双旋转操作调整解决。

在这里插入图片描述

删除导致失衡的操作先保留

有了二叉查找树和平衡二叉树,为什么还要设计红黑树?

二叉查找树的特点是:左子树的节点值比父亲节点小,而右子树的节点值比父亲节点大。可能出现一种极端的情况,输入一个有序链表时,二叉查找树就近似退化为一条链表,这样的二叉查找树的查找时间变成了O(n)。

平衡二叉树的特点是:具有二叉查找树全部特性的同时,每个节点的左子树和右子树的高度差至多等于1。这样就有效避免了上面二叉查找树出现的情况,能够把查找的时间控制在O(logn),不过却不是最佳的。因为平衡树要求每个节点的左子树和右子树的高度差至多等于1,这个要求实在是太严了,导致每次进行插入/删除节点的时候,几乎都会破坏平衡树的第二个规则,进而我们都需要通过左旋右旋来进行调整,使之再次成为一颗符合要求的平衡树。显然,如果在那种插入、删除很频繁的场景中,平衡树需要频繁着进行调整,这会使平衡树的性能大打折扣。为了解决这个问题,于是有了红黑树

红黑树

红黑树是一个平衡二叉搜索树。而且必须满足以下规则:

  1. 每个节点不是红色就是黑色(图中深色底纹代表黑色,浅色底纹代表红色,下同)。
  2. 根节点为黑色。
  3. 如果节点为红,其子节点必须为黑。
  4. 任一节点至 NULL(树尾端)的任何路径,所含之黑节点数必须相同。

**根据规则4,新增节点必须为红:根据规则3,新增节点之父节点必须为黑。**当新节点根据二叉搜素树的规则到达其插人点,却未能符合上述条件时,就必须调整颜色并旋转树形。
在这里插入图片描述

插入节点

红黑树插入删除节点后,迭代器可能会失效。
在这里插入图片描述

假设新节点为X,其父节点为 P,祖父节点为G,伯父节点(父节点之兄弟节点)为S,曾祖父节点为GG。现在,根据二叉搜索树的规则,新节点X必为叶节点。根据红黑树规则4,X必为红。若P亦为红(这就违反了规则了,必须调整树形),则G必为黑(因为原为RB-tree,必须遵循规则了)。于是,根据X的插人位置及外围节点(S和 GG)的颜色,有了以下四种考虑:

状况1:

S 为黑且X为外侧插人。对此情况,我们先对 P,G 做一次单旋转,再更改P,G 颜色,即可重新满足红黑树的规则了。

在这里插入图片描述

旋转以及改变节点颜色的目的是让二叉树满足红黑树的条件。

注意,此时可能产生不平衡状态(高度相差1以上)例如图中的A和B为pull,D或E不为null。这倒没关系,因为 RB-tree的平衡性本来就比 AVL-tree弱(**红黑树不用刻意维持高度差的绝对值小于1也能保持良好的平衡状态。**是的,经验告诉我们,RB-tree 的搜寻平均效率和 AVL-tree 几乎相等。

状况2:

S为黑且X为内侧插人。对此情況,我们必须先对 P,X做一次单旋转并更改G,X 颜色,再将结果对G做一次单旋转,即可再次满足红黑树规则3。

在这里插入图片描述

状况3:

s 为红且x 为外侧插人。对此情况,先对P和G做一次单旋转,并改变X的颜色。此时如果 GG 为黑,一切搞定。红黑树的父子节点可以都是黑色。

在这里插入图片描述

状况4:

S 为红且X为外侧插人。对此情况,先对P和G做一次单旋转,并改变X的颜色。此时如果GG 亦为红,还得持续往上做,直到不再有父子连续为红的情况。

在这里插入图片描述

为了避免状况4“父子皆为红色”的情况持续向RB-tree的上层结构发展,形成处理时效上的瓶颈,施行一个由上而下的程序:假设新增节点为A,那么延着根节点到A的路径,只要看到有某节点X的两个子节点皆为红色,就把X改为红色,并把两个子节点改为黑色。

在这里插入图片描述

如果X的父节点P亦为红色(注意,此时S绝不可能为红色)。就得像状况1一样地做一次单旋转并改变颜色,或是像状况2一样地做一次双旋转并改变颜色。

在此之后,节点35的插入就很单纯了:要么直接插入,要么插入后(若X的节点为红)再一次旋转(单双均可能)即可。

红黑树的头节点和根节点

在红黑树中,头节点和根节点是两个不同的概念。

头节点是一个特殊的节点,它不存储任何元素值,只用于表示红黑树的边界。头节点是一个指向 _Base_type 类型对象的指针,其中 _Base_type 是一个定义了节点基本结构的类。头节点的 _M_parent 成员变量指向红黑树的根节点,而 _M_left_M_right 则分别指向红黑树中的最小值和最大值。

红黑树的根节点是指红黑树的顶层节点,根节点的_M_parent成员指向头节点。通常来说,根节点在插入、删除、旋转等操作中需要特殊处理。

在实现中,红黑树的根节点的父节点是头节点。这是因为头节点是一个特殊的节点,它不存储任何元素值,只用于表示红黑树的边界。将根节点的父节点指向头节点,可以方便地处理根节点的插入、删除、旋转等操作。
在这里插入图片描述

key

const key_type &key(link_type x) { return KeyOfValue()(value(x)); }

在 C++ STL 中,KeyOfValue 是一个函数对象,它用于从容器中的元素中提取关键字。在红黑树中,每个节点都包含一个元素,其中包含了一个关键字和一个值。KeyOfValue 用于从节点的元素中提取关键字。

KeyOfValue() 表示创建一个 KeyOfValue 的实例,并调用其函数操作符(函数重载运算符 operator())来提取节点元素中的关键字。括号中的 value(x) 表示获取节点 x 的元素,即返回类型为 value_type& 的引用。因此,KeyOfValue()(value(x)) 表示对节点 x 的元素调用 KeyOfValue 函数对象的函数操作符,以提取其关键字。

模板参数 KeyOfValue

在STL源码中,红黑树的模板参数中的 KeyOfValue 是一个函数对象,用于从红黑树节点的 value 中获取键值。

红黑树是一种用于实现关联容器的数据结构,它的每个节点存储一个键值对,其中键值用于对节点进行排序和查找。在STL源码中,红黑树节点的定义类似于以下形式:

template <typename Value>
struct RbTreeNode {
    RbTreeNode* parent;
    RbTreeNode* left;
    RbTreeNode* right;
    Value value;
    bool color;
};

其中 Value 是节点存储的值类型,value 是节点存储的值。但是在红黑树的实现中,我们需要对节点进行排序和查找,因此需要从节点的 value 中获取键值。

由于 Value 类型的不同,获取键值的方式也可能不同,因此在红黑树的模板参数中,需要指定一个函数对象 KeyOfValue,用于从 value 中获取键值。例如,如果我们的红黑树节点的 value 是一个 std::pair<K, V> 类型,其中 K 是键值类型,那么可以定义一个 KeyOfValue 函数对象如下:

template <typename T>
struct Select1st {
    const typename T::first_type& operator()(const T& x) const {
        return x.first;
    }
};

template <typename K, typename V, typename Compare>
class RbTree {
public:
    typedef std::pair<K, V> value_type;
    typedef value_type* pointer;
    typedef value_type& reference;
    typedef Select1st<value_type> key_of_value;

    // ...
};

在上述代码中,Select1st 是一个函数对象,它的 operator() 函数返回一个 std::pair 的第一个元素,即键值 K。在 RbTree 模板参数中,key_of_value 指定了键值获取方式为 Select1st<value_type>,即从 value 中获取第一个元素作为键值。

copy

深拷贝

函数有两个参数:第一个参数link_type x是指向原始树根节点的指针,第二个参数link_type p是指向复制树中根节点父节点的指针。

该函数的工作流程如下:

  1. 首先,它创建一个名为top的新节点,作为根节点x的克隆,并将其父节点设置为p
  2. 然后,它检查x的右子节点是否存在。如果存在,它递归地复制以右子节点为根的子树,并将top的右子节点设置为复制子树的根。
  3. 然后进入一个循环,迭代地复制x的左子树。在循环内部,它创建一个新节点y作为x的左子节点的克隆,将其父节点设置为p,并将p的左子节点设置为y
  4. 如果x的右子节点存在,则递归地复制以右子节点为根的子树,并将y的右子节点设置为复制子树的根。
  5. 然后更新p指针为yx指针为x的左子节点。
  6. 循环会一直执行,直到xNULL
  7. 如果在复制过程中抛出异常,则调用erase()函数删除已经克隆的节点,然后重新抛出异常。
  8. 如果复制过程成功完成,则函数返回复制后的树的根节点top
template<class Key, class Value, class KeyOfValue, class Compare, class Alloc>
typename _rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::link_type
_rb_tree<Key, Value, KeyOfValue, Compare, Alloc>
::copy(link_type x, link_type p) {      //x是指向原始树根节点的指针,p是指向复制树中根节点的父节点的指针
    link_type top = clone_node(x);
    top->parent = p;

    try {
        if (x->right) {
            top->right = copy(right(x), top);
        }
        p = top;
        x = left(x);

        while (x != 0) {
            link_type y = clone_node(x);
            p->left = y;
            y->parent = p;
            if (x->right) {
                y->right = copy(right(x), y);
            }
            p = y;
            x = left(x);
        }
    }
    catch (...) {
        erase(top);
    }
    return top;         //返回复制后树的根节点
}

红黑树插入

不同的插入顺序得到的二叉树不相同

RB-tree提供两种插人操作:insert_anique ()insert_equal (),前者表示被插人节点的键值 (key)在整棵树中必须独一无二(因此,如果树中已存在相同的键值,插人操作就不会真正进行),后者表示被插人节点的键值在整棵树中可以重复,因此,无论如何插人都会成功(除非空间不足导致配置失败)。

//insert_equal,插入新值,节点键值允许重复。往下找到一个空的叶子节点,插入
    template<class Key, class Value, class KeyOfValue, class Compare, class Alloc>
    typename _rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::iterator
    _rb_tree<Key, Value, KeyOfValue, Compare, Alloc>
    ::insert_equal(const Value &v) {
        link_type y = header;
        link_type x = root();
        while (x != 0) {
            y = x;
            //取两个节点的键值进行比较:v的键值小于x的键值,往左,v的键值大于x的键值,往右
            x = key_compare(KeyOfValue()(v), _s_key(x)) ? left(x) : right(x);
        }
        //三个新参数为,x为新值的插入点,参数y为插入点的父节点,参数v为新值
        return __insert(x, y, v);
    }

    //insert_unique,插入新值,节点键值允许重复,若重复则插入无效
    template<class Key, class Value, class KeyOfValue, class Compare, class Alloc>
    std::pair<typename _rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::iterator, bool>
    _rb_tree<Key, Value, KeyOfValue, Compare, Alloc>
    ::insert_unique(const Value &v) {
        link_type y = header;
        link_type x = root();
        bool comp = true;
        while (x != 0) {
            y = x;
            comp = key_compare(KeyOfValue()(v), _s_key(x));
            x = comp ? left(x) : right(x);
        }//离开while循环后,y所指即插入点的父节点(此时,y节点必为叶子节点)
        iterator j = iterator(y);       //令迭代器j指向插入点的父节点y,其值是查找过程中最后一个比插入元素小的节点的迭代器
        if (comp) {                     //如果离开while循环时,comp为true,说明插入元素应该放在红黑树的最左端
            if (j == begin()) {         //说明红黑树为空,或者插入元素比红黑树中所有元素都要小
                return std::pair<iterator, bool>(__insert(x, y, v), true);      //插入到最左端
            } else {
                --j;        //将迭代器减一,使其指向最后一个比插入元素小的节点
            }
        }
        //最后一个比插入元素小的节点不等于 begin 迭代器,并且该节点的元素值不等于插入元素的值,
        // 则说明插入元素在红黑树中不存在,此时调用 _M_insert 函数插入元素
        if (key_compare(_s_key(j.node), KeyOfValue()(v))) {
            return std::pair<iterator, bool>(__insert(x, y, v), true);
        } else {    
            //如果 _M_key_compare(_S_key(__j._M_node), _KeyOfValue()(__v)) 的值为 false,
            // 说明最后一个比插入元素小的节点的元素值等于插入元素的值,此时不能将插入元素插入红黑树中。
            //返回最后一个比插入元素小的节点的迭代器和 false。
            return std::pair<iterator, bool>(j, false);
        }
    }

真正的插入函数

template<class Key, class Value, class KeyOfValue, class Compare, class Alloc>
    typename _rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::iterator
    _rb_tree<Key, Value, KeyOfValue, Compare, Alloc>
    ::__insert(base_ptr _x, base_ptr _y, const value_type &_v) {       //三个参数:x为新插入点,y为插入点的父节点,v为新值
        link_type x = (link_type) _x;
        link_type y = (link_type) _y;
        link_type z;

        //新插入节点的父节点等于头节点 || 说明要插入的值在红黑树中已存在 || 新插入点的键值小于父节点y的键值
        //为什么x != 0时,left(y) = z,传到这里的x参数不可能 != 0
        if (_y == header || x != 0 || key_compare(KeyOfValue()(_v), _s_key(y))) {
            z = create_node(_v);
            left(y) = z;            //插入到红黑树的左子树,

            //并更新相关指针
            if (y == header) {
                root() = z;
                rightmost() = z;
            } else if (y == leftmost()) {
                leftmost() = z;
            }
        } else {        //父节点不为头节点,并且新节点不存在于红黑树中
            z = create_node(_v);
            right(y) = z;
            if (y == rightmost()) {
                rightmost() = z;
            }
        }
        parent(z) = y;
        left(z) = 0;
        right(z) = 0;
        _rb_tree_rebalance(z, header->parent);
        ++node_count;
        return iterator(z);

    }

插入过程的四种状况解析

  • 可以在迭代器的基类中添加一个提取颜色的函数
bool get_color() const {
	return link_type(node)->color;
}
  • 以层序顺序插入,即可得到上文中的树
template<class Arg, class Result>
struct unary_function {
    typedef Arg argument_type;
    typedef Result result_type;
};

template<class T>
struct identity : public unary_function<T, T> {
    const T &operator()(const T &x) const {
        return x;
    }
};

template<class Arg1, class Arg2, class Result>
struct binary_function {
    typedef Arg1 first_argument_type;
    typedef Arg2 second_argument_type;
    typedef Result result_type;
};

template<class T>
struct less : public binary_function<T, T, bool> {
    bool operator()(const T &x, const T &y) const {
        return x < y;
    }
};

template<class T=mystl::_rb_tree<int, int, identity<int>, less<int>>>
void print_tree(T &tree) {
    typename T::iterator ite1 = tree.begin();
    typename T::iterator ite2 = tree.end();

    std::cout << "node.value: ";
    for (; ite1 != ite2; ++ite1) {
        std::cout << *ite1 << ':' << ite1.get_color() << ' ';
    }
    std::cout << std::endl;

}

int main() {
    mystl::_rb_tree<int, int, identity<int>, less<int> > itree;
    std::cout << itree.size() << std::endl;

    itree.insert_unique(30);
    itree.insert_unique(15);
    itree.insert_unique(70);
    itree.insert_unique(10);
    itree.insert_unique(20);
    itree.insert_unique(60);
    itree.insert_unique(85);
    itree.insert_unique(5);
    itree.insert_unique(25);
    itree.insert_unique(50);
    itree.insert_unique(65);
    itree.insert_unique(80);
    itree.insert_unique(90);
    itree.insert_unique(40);
    itree.insert_unique(55);
    print_tree(itree);
    //输出结果:0表示红色,1表示黑色
    //node.value: 5:0 10:1 15:1 20:1 25:0 30:1 40:0 50:1 55:0 60:0 65:1 70:1 80:0 85:1 90:0 

}

在这里插入图片描述

下面都四种状况依次解析:

说明:

  • 所谓的内侧和外侧插入的是针对最小的失衡树而言的。
  • 每种情况之间相互独立,每次测试一种
状况1

伯父节点为黑,新节点为外侧插入

插入3:itree.insert_unique(3);

在这里插入图片描述

insert_unique()中确定3在红黑树中是否已经存在,如果存在,直接返回,不存在则确定新增节点X要插入的位置和X的父节点Y以及插入节点的值v,调用__insert(x,y,v)

__insert(x,y,v)中,以值v创建节点z,插入到y的左子树,链接节点z和y(left(y) = z;parent(z) = y;left(z) = 0;right(z) = 0;),然后调用__rb_tree_rebalance(z,header->parent);

//状况1:参数为新增节点x,和红黑树的根节点
inline void __rb_tree_rebalance(__rb_tree_node_base *x, __rb_tree_node_base *&root) {
    x->color = __rb_tree_red;       //新节点必为红,不能有两个连续的红节点
    while (x != root && x->parent->color == __rb_tree_red) { //此时父节点为红
        if (x->parent == x->parent->parent->left) {//父节点为祖父节点的左节点,
            //令s为伯父节点,伯父节点为黑
            __rb_tree_node_base *s = x->parent->parent->right;          
            if (s && s->color == __rb_tree_red) {     //伯父节点为黑,跳过
                x->parent->color = __rb_tree_black; 
                s->color = __rb_tree_black;         
                x->parent->parent->color = __rb_tree_red;   
                x = x->parent->parent;
            } else {   //无伯父节点或伯父节点为黑
                if (x == x->parent->right) {      //外侧插入,跳过
                    x = x->parent;
                    __rb_tree_rotate_left(x, root);      //第一参数为左旋点
                }
                //S为黑,且外侧插入(新节点为父节点的左节点),对应状况1,只右旋
                //先改颜色后设定祖父节点为旋转点
                x->parent->color = __rb_tree_black;     //父节点改为黑
                x->parent->parent->color = __rb_tree_red;       //祖父节点改为红
                __rb_tree_rotate_right(x->parent->parent, root);     //第一参数为右旋点
            }
        } else {           //父节点为祖父节点的右节点,和左节点操作原理相同,
            __rb_tree_node_base *s = x->parent->parent->left;       //那伯父节点就是祖父节点的左节点
            if (s && s->color == __rb_tree_red) {
                x->parent->color = __rb_tree_black;
                s->color = __rb_tree_black;
                x->parent->parent->color = __rb_tree_red;
                x = x->parent->parent;          //把x置为祖父节点
            } else {
                if (x == x->parent->left) {  //内侧插入
                    x = x->parent;
                    __rb_tree_rotate_right(x, root);
                }
                x->parent->color = __rb_tree_black;
                x->parent->parent->color = __rb_tree_red;
                __rb_tree_rotate_left(x->parent->parent, root);
            }
        }
    }
    root->color = __rb_tree_black;

}

在这里插入图片描述

//右旋操作
inline void __rb_tree_rotate_right(__rb_tree_node_base *x, __rb_tree_node_base *&root) {
    __rb_tree_node_base *x_l = x->left;
    x->left = x_l->right;
    if (x_l->right != 0) {
        x_l->right->parent = x;
    }
    x_l->parent = x->parent;

    if (x == root) {
        root = x_l;
    } else if (x == x->parent->right) {
        x->parent->right = x_l;
    } else {
        x->parent->left = x_l;
    }
    x_l->right = x;
    x->parent = x_l;

}
状况2:

伯父节点为黑,且为内侧插入

itree.insert_unique(8);

insert_unique()中确定8在红黑树中是否已经存在,如果存在,直接返回,不存在则确定新增节点X要插入的位置和X的父节点Y以及插入节点的值v,调用__insert(x,y,v)

__insert(x,y,v)中,以值v创建节点z,插入到y的右子树,链接节点z和y(left(y) = z;parent(z) = y;left(z) = 0;right(z) = 0;),然后调用__rb_tree_rebalance(z,header->parent);

//参数为新增节点x,和红黑树的根节点
inline void __rb_tree_rebalance(__rb_tree_node_base *x, __rb_tree_node_base *&root) {
    x->color = __rb_tree_red;       //新节点必为红,不能有两个连续的红节点
    while (x != root && x->parent->color == __rb_tree_red) {  //如果父节点为红
        if (x->parent == x->parent->parent->left) {   //如果父节点为祖父节点的左节点,
            __rb_tree_node_base *s = x->parent->parent->right;          //令s为伯父节点
            if (s && s->color == __rb_tree_red) {     //S不存在,跳过
                ...
            } else {   //无伯父节点或伯父节点为黑
                if (x == x->parent->right) {      //S为黑且内侧插入,对应状况2;要旋转两次,先左旋再右旋
                    x = x->parent;				//把x的父节点作为左旋点
                    __rb_tree_rotate_left(x, root);      //左旋
                }
                //S为黑,且外侧插入新节点为父节点的左节点,对应状况1,只右旋
                x->parent->color = __rb_tree_black;     //父节点改为黑
                x->parent->parent->color = __rb_tree_red;       //祖父节点改为红
                __rb_tree_rotate_right(x->parent->parent, root);     //第一参数为右旋点
            }
        } else {           //父节点为祖父节点的右节点
            ...
        }
    }
    root->color = __rb_tree_black;

}

在这里插入图片描述

先左旋:

inline void __rb_tree_rotate_left(__rb_tree_node_base *x, __rb_tree_node_base *&root) {
    __rb_tree_node_base *x_r = x->right;
    x->right = x_r->left;
    if (x_r->left != 0) {
        x_r->left->parent = x;
    }
    x_r->parent = x->parent;

    if (x == root) {
        root = x_r;
    } else if (x == x->parent->left) {
        x->parent->left = x_r;
    } else {
        x->parent->right = x_r;
    }
    x_r->left = x;
    x->parent = x_r;
}

再右旋:

//右旋操作
inline void __rb_tree_rotate_right(__rb_tree_node_base *x, __rb_tree_node_base *&root) {
    __rb_tree_node_base *x_l = x->left;
    x->left = x_l->right;
    if (x_l->right != 0) {
        x_l->right->parent = x;
    }
    x_l->parent = x->parent;

    if (x == root) {
        root = x_l;
    } else if (x == x->parent->right) {
        x->parent->right = x_l;
    } else {
        x->parent->left = x_l;
    }
    x_l->right = x;
    x->parent = x_l;

}
状况3:

伯父节点为红,且为外侧插入

itree.insert_unique(75);

insert_unique()中确定8在红黑树中是否已经存在,如果存在,直接返回,不存在则确定新增节点X要插入的位置和X的父节点Y以及插入节点的值v,调用__insert(x,y,v)

__insert(x,y,v)中,以值v创建节点z,插入到y的右子树,链接节点z和y(left(y) = z;parent(z) = y;left(z) = 0;right(z) = 0;),然后调用__rb_tree_rebalance(z,header->parent);

//参数为新增节点x,和红黑树的根节点
    inline void __rb_tree_rebalance(__rb_tree_node_base *x, __rb_tree_node_base *&root) {
        x->color = __rb_tree_red;       //新节点必为红,不能有两个连续的红节点
        while (x != root && x->parent->color == __rb_tree_red) {  //父节点为红
            if (x->parent == x->parent->parent->left) {   //父节点为祖父节点的左节点,
                __rb_tree_node_base *s = x->parent->parent->right;          //令s为伯父节点
                if (s && s->color == __rb_tree_red) {     //S为红
                    x->parent->color = __rb_tree_black; //把x父节点的颜色改为黑
                    s->color = __rb_tree_black;         //把伯父节点的颜色改为黑
                    x->parent->parent->color = __rb_tree_red;   //把祖父的节点颜色改为红
                    x = x->parent->parent;				//继续往上检查
                } else {  //跳过,进入下一轮循环,如果曾祖父节点为黑,则不需要旋转,这个函数就结束了
                    if (x == x->parent->right) {       
                        x = x->parent;
                        __rb_tree_rotate_left(x, root);      
                    }
                    //S为黑,且外侧插入新节点为父节点的左节点,只右旋
                    x->parent->color = __rb_tree_black;     //父节点改为黑
                    x->parent->parent->color = __rb_tree_red;       //祖父节点改为红
                    __rb_tree_rotate_right(x->parent->parent, root);     //第一参数为右旋点
                }
            } else {           //父节点为祖父节点的右节点
                ...
            }
        }
        root->color = __rb_tree_black;

    }

在这里插入图片描述

在使用源代码调试之后发现,状况3这种情况是不需要进行旋转的,节点的颜色改变后就可以满足红黑树的条件。

状况4:

itree.insert_unique(35);

伯父节点为红,为外侧插入,且曾祖父节点为红。

//参数为新增节点x,和红黑树的根节点
inline void __rb_tree_rebalance(__rb_tree_node_base *x, __rb_tree_node_base *&root) {
    x->color = __rb_tree_red;       //新节点必为红,不能有两个连续的红节点
    while (x != root && x->parent->color == __rb_tree_red) {  //父节点为红
        if (x->parent == x->parent->parent->left) {   //父节点为祖父节点的左节点,
            __rb_tree_node_base *s = x->parent->parent->right;          //令s为伯父节点
            if (s && s->color == __rb_tree_red) {     //S为红,第二轮循环跳过
                x->parent->color = __rb_tree_black; //把x父节点的颜色改为黑
                s->color = __rb_tree_black;         //把伯父节点的颜色改为黑
                x->parent->parent->color = __rb_tree_red;   //把祖父的节点颜色改为红
                x = x->parent->parent;				//继续往上层查找,进行第二轮循环
            } else {   //第二轮:无伯父节点或伯父节点为黑
                if (x == x->parent->right) {      //第二轮:外侧插入,跳过
                    x = x->parent;
                    __rb_tree_rotate_left(x, root);      //第一参数为左旋点
                }
                //S为黑,且外侧插入新节点为父节点的左节点,对应状况1,只右旋
                x->parent->color = __rb_tree_black;     //父节点改为黑
                x->parent->parent->color = __rb_tree_red;       //祖父节点改为红
                __rb_tree_rotate_right(x->parent->parent, root);     //第一参数为右旋点
            }
        } else {           //父节点为祖父节点的右节点
           ...
        }
    }
    root->color = __rb_tree_black;

}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值