复习红黑树(一)

      复习一下红黑树。红黑树算是大学时候学过的,比较复杂的算法了。。   复习了一下,还是比较生涩,只能下班看看,不是一天就能学完的。

      首先,红黑树继承自二叉搜索树,所以二叉搜索树的操作,对红黑树也适用。

     但他也是一种高效的相对平衡的二叉树。说他相对平衡,是指他的平衡性比AVL差很多,但比一般的搜索树平衡性好多了。先看一下红黑树的定义:

1)每个结点要么是红的,要么是黑的。
2)根结点是黑的。
3)每个叶结点,即空结点(NIL)是黑的。
4)如果一个结点是红的,那么它的俩个儿子都是黑的。
5)对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点。

 

   红黑树是一颗相对平衡的树。因为插入和删除都有相应的调整操作,所以红黑树正常情况下,每个叶子节点到根节点的红黑节点的个数是均匀分布的。又因为特性5),所以红黑树的查询路径,最坏的情况下不会比最好的情况下多查询超过两倍的节点,这也保证了红黑树最坏情况下的查询复杂度。

   But,为嘛javatreemaplinux内核算法都那么青睐红黑树? 大神自有大神的计算,肯定是通过实验对比得出的结论。

 

   刚开始看红黑树的时候有过一个疑惑,树干嘛要涂上颜色,AVL树不是挺好的嘛,平衡性好,效率高。这次再回过头看红黑树,发现可以有自己的一点看法,这也算一点进步吧:

   为啥红黑树要有颜色?这个颜色的第一个作用,是用来调整平衡的。   即红黑树通过第45条特性,保证了他的相对平衡,虽然他的平衡性不如AVL树,但是也能保证O(logN)的复杂度。这绝对是比二叉搜索树优秀得多的地方。

   这个颜色的第二个作用,就是用来保证自身的调整没有AVL树那么复杂。他的平衡性低于AVL树,所以查询效率略低,但是菜不够,饼来凑,查询不行,因为插入,删除要优于AVL树,整体效果就不一定了。大神们摒弃AVL树,采用红黑树也说明这点。

扫描二维码关注公众号,回复: 727822 查看本文章

   插入的时候,两种树的复杂度差不多,都是常数级的,但是如果插入的节点的父节点是黑色,红黑树是不需要做调整的。这种情况红黑树略优于AVL,但是几乎可以忽略不计,红黑树真正由于AVL是由于删除的时候(很多地方都说红黑树和avl树的插入时间复杂度是O(logN),那是因为他们的调整需要从下往上比较,经过logN次比较,但是真正的调整操作却只需要常数次(1-3))。删除的时候AVL却是O(logN)的,因为没有平衡因子的羁绊,所以删除的时候红黑树也是常熟级的删除操作。但是在代码实现上红黑树要比AVL树复杂的多。

  为什么AVL树的删除可能会删除logN次?  个人一点看法:调整,就是旋转,是可能减少整棵树的高度的,但是没有一种调整是可以增加树的高度的。所以当因为新增产生的不平衡时,通过调整,产生不平衡的节点的高度会变的和原来一样了,但是如果因为删除而发生了不平衡,那么很可能整棵树,乃至这棵树的祖先各节点都可能发生不平衡,因为调整不会增加节点的高度。看下面一个删除的小例子:



 
 把绿色的节点删除,那么经过一次调整后,整棵树的高度会由3变为2,如果这棵树上面还有父节点,那么很可能父节点也会因为这棵树高度的变化,而产生不平衡,依次类推。。。。

 

   其实  如果你的树查询很多,但是删除几乎没有的话,AVL树的性能,是优于红黑树的。

   具体说说红黑树:

   搜索没啥可说的,所有的二叉搜索树都遵循一个规则。

   插入:

   首先,把节点插入到相应的位置,没啥说的,二叉搜索树的插入。

   把节点默认设置成红色。

   为啥默认红色?其实默认红黑都可以的,默认黑色的话,条件5是肯定被破坏了;默认是红色的话,条件5是肯定满则的,条件4是可能被破坏了。当然是哪个方便哪个来。 如果节点默认是黑色,那么无论如何,都要进行调整,因为条件5是肯定不满足了。  如果默认是红色呢?条件5是肯定满足的,条件4可能被破坏(50%概率),从这个角度来说,红色比较好。

   再考虑一下这个问题:(自己的想法,大神可能不同意):

   如果默认是插入黑色节点,那么有一个问题,因为原来红黑树是平衡的,黑色节点是固定的,这时候突然插入一个来,必定有一条线,根节点到叶子节点的路径比其他线要长。无论怎么调整,肯定会有一条线路的黑色节点是多一个的。。   这时候,怎么判断把这条线路的哪个黑色节点变红?这是个难题。。。 而且,既然又要调整,又要把某个节点变红,为啥不一开始就插进去个红色的?

 

   当然,如果有大神能想出比较好的办法,插入默认为黑色也能方便的处理,也是可以的,只要插入后的树满足5项基本原则即可,其他随我们编码。

    红黑树的插入:

     插入分三种情况:

     第一,如果插入的是根节点,即原来是空树,那么直接插入,然后改色即可。

     第二,如果插入的节点父节点是黑色的,那么直接插入即可。

     第三,如果插入的节点的父节点是红色的,那么,插入的节点肯定有爷爷,且爷爷是黑色的,也有叔叔(叔叔即当前节点的父节点的父节点的另一个子节点,即使是空节点),

    ①如果叔叔节点是红色的,那么把叔叔和父亲节点置为黑色,爷爷置为红色,这样以爷爷节点为根节点的子树是符合红黑树规则的(如图),这样递归处理,直到退出循环或者爷爷节点为根节点,把爷爷节点变成黑色即可。

 
 ②如果插入节点的叔叔节点是黑节点,那么有几种情况,类似于AVL树的操作:

   如果插入的节点的父节点是爷爷节点的左孩子,并且插入的是父节点的左孩子:

   相当于AVL树插入左孩子导致不平衡,以父节点为轴顺时针旋转即可:

    

 
 旋转完出现两个问题: 以原来父节点为跟的右子树,比左子树多一个黑色节点,父节点跟新插入的节点都是红色的,这两种情况可以用把父节点(新树的根节点)置为黑色,爷爷节点(新树根节点的右子孩子)置为红色来处理,变成了:

 
红黑树了有么有。  跟AVL的处理其实差不多,只是多了几个节点的赋值操作而已。

  相应的,还有其他3中情况:

  2,新插入节点在父节点的右孩子,父节点是爷爷节点的左孩子:

  相当于AVL树中的在左孩子的右子树上插入节点导致不平衡,先以父节点为轴逆时针,然后以新节点为轴顺时针,最后将新树的根节点变成黑色,新树根节点的左右孩子都置为红。

           3,新插入的节点在父节点右孩子,父节点是爷爷的右孩子,顺时针;

           4,新插入的节点在父节点的左孩子,父节点是爷爷的右孩子,先顺再逆即可。

(ps:以上3,4最后都是将新树的根节点置为黑色,将新树根节点的左右孩子都置为红色,这里说的树是个相对概念,如上图中爷爷节点上可能还有父节点,祖宗辈的,但是因为调整之后的树是红黑树,而且从调整后的子树的根节点到叶子节点经历的黑色节点个数不变,所以,子树调整后,整棵树还是红黑树,不必考虑祖宗。。)

 红黑树的插入,其实理解了AVL树之后,是很容易的,他跟AVL的插入差不多,只不过多了几个固定的染色过程而已。红黑树的删除是比较麻烦的,明天再写把,今天看不完了。。   我先好好消化一下。。。

 

 

 

猜你喜欢

转载自709002341.iteye.com/blog/2259248
今日推荐