• 红黑树插入操作


        原本是在一张纸上画出了红黑树插入操作的所有情况的演变图示,但前两天将删除操作写到博客上,因此想来,索性就将插入操作也一并写上,以免日后图纸丢失,也方便大伙共同研究,学习。

    红黑树的插入操作

     

    1:节点命名约定

    N表示新添加的节点。即:取 New 的首字母;

    P 表示父节点。即:取 Parent 的首字母;

    S表示兄弟姐妹节点。即:取 Sibling的首字母;

    G表示祖父节点。即:取 Grandfather的首字母;

    Nil表示叶子节点。即所谓的空节点;注意:红黑树中的叶子节点与其他树中所述说的叶子节点不是同一概念。而且红黑树中的叶子节点(即:Nil节点)永远是被定义为黑色的。

     

    2:插入操作宏观分析

    对于二叉搜索树的插入操作总是插入在某个叶子节点处(注意:此处的叶子并非指rb-treeNil叶子节点,而是真实的叶子节点),而rb-tree的插入也不例外。另外,rb-tree的插入操作我们总是以红色节点插入,如此我们可以保证rb-tree的黑节点数的任何分支上的平衡。当然以红色节点插入,就有可能违背任何父子节点不能同时为红的性质,所以此时需要额外做一些调整处理。

    因此,在红黑树中,插入一个节点往大的说,只有以下几种情况:

    情况一:红NP;(其中N又分为在P的左子树插入以及右子树插入)

    情况二:红NPS;(其中N又分为在P的左子树插入以及右子树插入)

    情况三:红NPS;(其中N又分为在P的左子树插入以及右子树插入)

     

    其中情况一,插入后的rb-tree的任何性质都没有被破坏,因此不需要做任何调整处理。情况二和情况三插入后均违背了任何父子节点不能同时为红的性质,所以下面我们将全面讨论如何通过调整,重新让最终的rb-tree合法化。

     

    3:红黑树插入后平衡处理

    在具体分析之前,再次列出红黑树的定义:

    1)       任何一个节点非红即黑;

    2)       树的根为黑色;

    3)       叶子节点为黑色(注意:红黑树的所有叶子节点都指的是Nil节点)

    4)       任何两个父子节点不可能同时为红色;

    5)       任何节点到其所有分枝叶子的简单路径上的黑节点个数相同;

     

        下面是几个图示说明:

     

     

    根据前面的分析,插入节点的所有情况罗列如下:

     

    a)       NP,且N为左子节点插入

    分析:该情况则新插入的节点N,在插入后不会破坏任何性质,因此插入结束。此时,我们还可推测P的右子树,要么是一个红色节点要不就是一个Nil节点。

     

     

    b)       NP,且N为右子节点插入

     

        分析:该情况则新插入的节点N,在插入后不会破坏任何性质,因此插入结束。此时,我们还可推测P的左子树,要么是一个红色节点要不就是一个Nil节点。

     

     

    c)       NPS,且N为左子节点插入

    根据rb-tree的性质,P为红,则P的父节点,即:对N来说就是G节点,必为黑色节点,否则违背性质4。再根据性质5,则我们可以推测出此时的S节点,也必为Nil节点。否则经过G节点的两个分支的黑节点数将不平衡,即原rb-tree就已经不平衡。

     

    分析:插入后,明显的G的左子树深度变为2,右子树却为0(注意:此时SNil节点),因此需要以G节点进行一次右旋转。旋转后再将PG的颜色变换,此时原本经过G的节点的任何路径黑节点数为2,现在经过P节点的任何路线的黑节点数也为2,此时所有性质都满足:

    操作:

    ð  G右旋转

    ð  P由黑色改为红色

    ð  G改为红色,此时整棵树已完全满足rb-tree的所有性质。

    说明:其实这种情况就是AVL树的LL插入的情况。

     

    d)       NPS,且N为右子节点插入。

     

    这种情况下,其实我们可以通过一次旋转操作,转变成前面的c)状态。因为对于红P而言,现在新插入的红N位置,在未插入N时,该位置必为Nil节点。因此,P的左子节点也必为Nil节点(不明白的同学,请看性质)。因此,在插入N后,我们可以简简单单地对P做一次左旋转,就转变成了c)情况。只是旋转后的P节点相当于c)中的N,即:此时将P作为新插入的节点,再进行调整即可。

     

    分析:经过上面左旋转后,此时将P节点当作是新插入的节点”N”,再转向c)情况处理即可。

    操作:

    ð  P左旋转

    ð  以旋转后的P节点作为新插入的节点”N”

    ð  转向c)情况处理。

    说明:这种情况其实就是AVL中的LR插入操作。

     

    e)       NPS,且N为左子节点插入

     

    根据rb-tree的性质,红PS,则G必为黑。因此,原先经过G节点的黑节点数必为2。且原先的PS的左、右子节点必都为Nil

     

    分析:旋转前,原本经过G节点的黑节点数为2。旋转后,原本经过G的节点却变成经过P节点。而经过P的右分支的黑节点数为2不变,但经过P的左子树的黑节点数却变为1,减少了1,因此,直接将新插入的节点N由红色改为黑色。如此,不论是原本经过G的还是现在经过P的,黑节点数都是平衡的。但由于原本的G位置是黑色的,现在却变成红色。因此,可以将旋转后的P节点当成是新插入的节点,再转向a)情况处理即可。(即:所谓的上溯)。

    操作:

    ð  G右旋转

    ð  N节点由红色改为黑色

    ð  以此时的P节点作为新插入的节点”N”,转向a)情况处理即可。

    说明:这种情况就是AVL中的LL插入操作。

     

    f)       NPS,且N为右子节点插入

     

    根据rb-tree的性质,红PS,则G必为黑。因此,原先经过G节点的黑节点数必为2。且原先的PS的左、右子节点必都为Nil。并且有了前面的e)点分析,其实不难想到,我们可以将f)情况通过简简单单的一个左旋转操作,将问题变换为e)情况。

     

    分析:将P左旋转后,此时将旋转后的P节点当成是将插入的节点”N”,则情况就跟e)点完全一样。

    操作:

    ð  P左旋转;

    ð  P作为新插入的节点”N”

    ð  转向e)情况处理即可。

    说明:这种情况就是AVL中的LR插入操作。

     

    至此,红黑树的L型(即:LLLR)插入全部介绍完毕。对于R型(即:RLRR)插入的情况,仅仅只是上面b)c)d)e)f)情况中的旋转方向相反一下即可。思路完全相同。另外,与红黑树的删除操作相比,插入操作显得非常的直观,没有删除操作那么复杂。其实将上述的a)f)情况绘制在一张纸上的话,一张纸就可以完全说明清楚以上全部情况。(有兴趣的同学可以试下看)。

    另外,还有一个小细节需要注意,如果在上溯的过程中,如果已经到了树的根了,即:如上面的e)情况中,则此时直接将节点P改为黑色即可。因为已经到根了,P即为根节点。根据性质2,根节点必为黑色。并且此时将P改为黑色,则rb-tree的任何性质都不会被破坏,插入结束。

     

    Q&A

    前面e)情况中,为什么要旋转再变色,而不直接改变G为红色,改变P为黑色,改变S为黑色,然后再将G为新插入的节点转到a)情况处理?

     

    ð  个人的理解:要这么转换也是可以的,但从操作步骤数上,将比上文中e)的操作来的多,因此花的时间也将更多。

     

    以上为个人理解,有错误之处,欢迎指正!

  • 相关阅读:
    运营活动总结
    《天天来塔防》游戏分析
    C++ 头文件与using namespace std
    cocos2dx -- 错误笔记(4)ntdll.dll堆已损坏
    大学,且行且珍惜
    cocos2dx -- 错误笔记(3)class类型重定义
    谈谈对HTML语义化的理解
    CSS深入研究:display的恐怖故事解密(2)
    We have a problem with promises
    react-组件生命周期
  • 原文地址:https://www.cnblogs.com/tongy0/p/5468267.html
Copyright © 2020-2023  润新知