连贯的学习黑树(插入节点)

红 - 黑树是一种平衡二叉树被广泛用于,但对于很多人谁是新算法,红黑树实在是太复杂,看之前July博文,觉得自己的写作非常具体的。不过还是有点乱。不能急。要每天看一点点,所以我把插入和删除分开来写,仅仅要看懂并记住插入后是如何操作的,那么删除也就easy了。

红黑树的规则:

性质1. 节点是红色或黑色。

性质2. 根是黑色。

性质3. 全部叶子都是黑色(叶子是NIL节点)。

性质4. 每一个红色节点的两个子节点都是黑色。(从每一个叶子到根的全部路径上不能有两个连续的红色节点)

性质5. 从任一节点到其每一个叶子的全部简单路径都包括同样数目的黑色节点。

最基本的两条是4和5。一旦插入一个新的节点,那么4和5的性质就有可能破坏。从5中我们能够推出新增节点必须设为红色,而再依据4。新增节点的父节点必须是黑色。

我们推出的这最后一条很重要。由于一旦发现插入节点要插在一个红色节点下,就要開始折腾了!

红黑树的最基本操作是旋转(左旋和右旋)。因为对称性,将一个即可。

下图介绍右旋的一个样例(本文图皆来自《STL源代码剖析》)

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamlfemFpbHVzaGFuZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" >

我们从图中发现,右旋事实上就是改变了k1,k2的父子关系,那么其它节点怎么变呢?

能够这样理解。父亲要和儿子换位置,父亲好悲剧,那父亲的还有一个儿子和他的父子关系不能变(再变就太慘了),儿子长辈分了,那么儿子就把他的儿子交给原来的父亲当儿子吧,而操作是右旋,那就移动儿子的右孩子。

简记:右旋:孩子的右孩子给父亲当左孩子。

左旋:孩子的左孩子给父亲当右孩子。

默念三遍。就会发现旋转的思路就是这么简单!

代码例如以下(STL源代码。大师作品):

inline void _rb_rotate_right(_rb_tree_node_base* x,_rb_tree_node_base*& root)\\x为k2。root为根节点
   _rb_tree_node_base* y=x->left;   \\y为左孩子。也就是k1
    x->left=y->right;  \\孩子的左孩子成为父亲的右孩子 B接k2
    if(y->right!=0)
      y->right->parent=x;  \\  回马枪设定父节点
    y->parent=x->parent; \\k1開始跑到k2的位置
    if(x==root)
      root=y;
     else if(x==x->parent->right)
       x->parent->right=y;   \\y代替x的地位,但要和x的原父亲的关系还原
     else
     x->parent->left=y;
     y->right=x;
     x->parent=y;  \\最后,父亲变成孩子,悲剧。
}

代码思路:1先把孩子的左孩子接到这个节点的右孩子上(2句)  2这个右孩子的父亲是这个节点(1句) 3、节点的左孩子代替节点的位置(1句)  4、和原节点父亲节点的关系保留(3种推断)  5、节点成为孩子节点的孩子(2句)

以下进入正题:插入节点

插入节点可能会带来3种不同情况的破坏,下图给出的是4种情况:(4仅仅是3的更复杂一点的情况)

我们细致观察这三种不同情况,同样的是插入节点的父亲都是红的。不同点有2:1是父亲的兄弟节点(叔节点)是什么颜色的?2新增节点是作为左孩子还是右孩子?

第一种情况:

叔黑+左孩子(黑左bl)

解决方法:右旋。

例如以下图:

说是右旋,旋哪个呢?新增节点肯定不动,以新节点的父亲P,爷爷为轴右旋G。旋转后这两个节点颜色都变(新增点肯定是红的)。

简记:黑左(bl)=右旋PG

助记:bl=r+pg(Boys’ Love RPG)

另外一种情况:

叔黑+右孩子(br)

解决方法:左旋+右旋

例如以下图:

我们从图中能够看出,先左旋X和P,再右旋X和G(变化X和G)。细致想想,第一次左旋似乎就是构造出第一种情况(图2先把8和10的颜色变化了,应是8红,10黑,那就和第一种情况一样)。

简记:黑右(br)=左旋XP。到达叔左。

助记:嘿哟,郎咸平(lXP)

第三种情况

叔红

这样的情况很easy。我们肯定叔节点不能有孩子了,有的话必须是黑色的,那叔节点那条分支的黑色就多了,所以我们能够随便改变叔节点的颜色(这是前提)。

例如以下图(来自July)

4是新增节点,4和5都红了。那就把5变黑,5黑后为了平衡的把8(叔节点)也变黑,都黑了后7这条分支比1分支多了一个黑色节点。所以7也要红,这样和2又都是红色的了,还得往上递归。这时候7相当玉新插入节点。得三种情况推断(图中这个符合另外一种情况),一直递归到根节点。

简记:变变变(PGU都变了),再推断。

最后来看SLT里面大师是如何写源码的。

首先,他的思路有所变换,他感觉第三种情况一直往上递归太慢,所以他採用了先处理的方法,例如以下图:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamlfemFpbHVzaGFuZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" >

大致思路是:沿着插入节点往上看。假设有一个节点的两个子节点都是红色。那就把这个节点改成红色,他的子节点都改成黑色。假设此时这个节点的父节点也是红色(此时父节点的兄弟节点一定是黑的,请自己分析)就要像第一种或者第一种情况处理。

最后,插入节点的推断就仅仅剩第1和第2两种情况。

代码例如以下:

// 这是一个全局函数
// 又一次令树形平衡(改变颜色及旋转树形)
inline void _rb_tree_rebalance(_rb_tree_node_base* x , _rb_tree_node_base*& root)   //x新增节点,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* y = x->parent->parent->right;    // 令y为伯父节点
            if(y && y->color == _rb_tree_red)    // 伯父节点存在。且为红(第三种情况)
            {
                x->parent->color = _rb_tree_black;           // 更改父节点为黑色
                y->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);    // 第一个參数为左旋点
                }
                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* y = x->parent->parent->left;    // 令y为伯父节点
            if(y && y->color == _rb_tree_red)    // 有伯父节点,且为红
(第三种情况) 

            {
                x->parent->color = _rb_tree_black;           // 更改父节点为黑色
                y->color = _rb_tree_black;                   // 更改伯父节点为黑色
                x->parent->parent->color = _rb_tree_red;     // 更改祖父节点为红色
                x = x->parent->parent;          // 准备继续往上层检查
            }
            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);    // 第一个參数为左旋点
            }
        }
    }//while
    root->color = _rb_tree_black;    // 根节点永远为黑色
}  

事实上,红黑树确实挺复杂的,变化太多,但仅仅要略微注意下几种情况的相互关系,比方3->2->1,理解就简单多了。

下一步学习删除节点。

版权声明:本文博客原创文章,博客,未经同意,不得转载。

时间: 2024-08-09 19:21:21

连贯的学习黑树(插入节点)的相关文章

红黑树插入详解

查找二叉树插入节点: 已知查找二叉树的性质为根节点的值大于左子树的值,小于右子树的值,按照此规律,根据要插入的值为其寻找合适的插入位置,最后将其插入即可: Tree-Insert(T,z) { x = root(T); y = NULL; while(x != NULL) { y = x; if(key[z] < key[x]) x = left[x]; else x = right[x]; } p[z] = y; if(y == NULL) root[T] = z; else if(key[z

红黑树插入删除节点过程分析 &amp;&amp; C代码实现

红黑树的插入和删除规则: 红黑树的五个性质 1.    每个节点要么是红的,要么是黑的 2.    根节点时黑色的 3.    每个叶节点(叶节点既指树尾端NIL指针或NULL节点)是黑色的 4.    如果一个节点时红的,那么它的两个儿子都是黑色的 5.    对每个节点,其到叶节点树尾端NIL指针的每一条路径都包含相同数目的黑节点 这里所说的"叶节点"或者"NULL节点",它不包含数据而只充当树在此结束的知识. 二叉树的左旋和右旋这里不再讲解 红黑树的插入操作:

jQuery学习-属性访问,插入节点

//属性访问$(function(){ //1.操作文本节点:通过jQuery 对象的text()方法 alert($("#bj").text()); $("#bj").text("123") //2.操作属性节点:通过jQuery对象的 attr()方法. //注:直接操作value属性值可以使用更便捷的val()方法. alert($(":text[name='username']").attr("value&q

红黑树 插入

红黑树是一种二叉搜索树,每个节点增加一位来储存节点的颜色,红或黑. 红黑树通过如何一条从跟到叶子的路径上各个节点的着色方式的限制,红黑树确保没有一条路径比另一条路径长两倍. 其他性质: 根节点是黑色. 红色节点的子节点都为黑色. 叶子节点或NULL空节点都为黑色.所有原来的叶子节点都有NULL的空节点作为新的叶子节点,取代了自己的叶子节点的身份,即,所有的NULL节点都是黑色的意思. 最重要一点:任何一节点到叶子节点的NULL指针的路径经过的黑色节点数相同. 插入: 左旋 以节点p为支点的话,即

jdk源码分析红黑树——插入篇

红黑树是自平衡的排序树,自平衡的优点是减少遍历的节点,所以效率会高.如果是非平衡的二叉树,当顺序或逆序插入的时候,查找动作很可能会遍历n个节点 红黑树的规则很容易理解,但是维护这个规则难. 一.规则 1.每个节点要么是红色.要么是黑色 2.根节点一定是黑色 3.红色节点不可以连续出现(父节点.子节点不可同时为红) 4.从任意节点出发,到树底的所有路线,途径的黑节点数量必须相同 在修改红黑树的时候,切记要维护这个规则.一般默认插入红色节点(除非是root节点),插入后再进行旋转和颜色变换 二.旋转

红黑树插入和删除的情况分析

红黑树是特殊二叉查找树的一种,一颗红黑树有以下5种性质: 1.根节点为黑色. 2.每个节点不是黑色就是红色. 3.每个红色节点的两个儿子一定是黑色. 4.所有的叶子节点都是黑色.(注:这里的叶子节点并不是真正意义上的叶子节点,而是一种只有颜色属性但不存放数据的节点,而且其没有儿子节点) 5.一个红黑树的中任取一个节点,从它所在位置到其他任何叶子节点的简单路径上所经过的黑色节点数相同. 这2个性质决定了从根节点到叶子节点的最长路径不可能大于最短路径的2倍.所以红黑树是一个大致平衡的二叉树.但跟AV

红黑树-插入操作

红黑树的五个性质: 1)每个结点要么是红的,要么是黑的. 2)根结点是黑的. 3)每个叶结点,即空结点(NIL)是黑的. 4)如果一个结点是红的,那么它的俩个儿子都是黑的. 5)对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点. 红黑树插入的几种情况: 1.树是空的,直接将节点设置为根节点,颜色为黑: public void case1(RBnode T,RBnode newNode){        if(newNode.getParent()==null){         

算法导论 之 红黑树 - 插入[C语言]

作者:邹祁峰 邮箱:[email protected] 博客:http://blog.csdn.net/qifengzou 日期:2013.12.24 21:00 转载请注明来自"祁峰"的CSDN博客 1 引言 在之前的博文中,本人对平衡二叉树的处理做了较详尽的分析,有兴趣的朋友可以参阅博文<算法导论 之 平衡二叉树 - 创建 插入 搜索 销毁>和<算法导论 之 平衡二叉树 - 删除>.平衡二叉树AVL是严格的平衡树,在增删结点时,其旋转操作的次数较多:而红黑树

查找结点,创建节点,插入节点

1.查找节点介绍 2.创建节点 3.插入节点 4.程序(查找,并操作属性与文本节点) 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Insert title here</title> 6 <script type="text/javascript" src="jquery-3.2.1.m