红黑树——2.插入

      红黑树是一种自平衡二叉查找树。它的统计性能据说要好于平衡二叉树(AVL树),因此,红黑树在很多地方都有应用。在C++ STL中,很多部分(目前包括set, multiset, map, multimap)应用了红黑树的变体(SGI STL中的红黑树有一些变化,这些修改提供了更好的性能,以及对set操作的支持)。它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除等操作。
      牢记着5条性质:
        性质1. 节点是红色或黑色
        性质2. 根是黑色
        性质3. 所有叶子都是黑色(叶子是NIL节点)
        性质4. 如果一个节点是红的,则它的两个儿子都是黑的
        性质5. 从任一节点到其叶子的所有简单路径都包含相同数目的黑色节点。

对红黑树的插入操作和删除操作会导致不再符合红黑树的性质,可以通过重图颜色和旋转节点来保持红黑树的特性。
所以,请记住对红黑树的所有操作,目标都是为了保持其特定的性质。以下一些做法,你可以先记住他,以后慢慢理解。

插入操作
插入操作可以概括为以下几个步骤:
    (1) 查找要插入的位置,时间复杂度为:O(N)
    (2) 将新节点的color赋为红色
    (3) 自下而上重新调整该树为红黑树
【 注)第(1)步的查找方法跟普通二叉查找树一样,第(2)步之所以将新插入的节点的颜色赋为红色,是因为:如果设为黑色,就会导致根到叶子的路径上有一条路上,多一个额外的黑节点,这个是很难调整的。但是设为红色节点后,可能会导致出现两个连续红色节点的冲突,那么可以通过颜色调换(color flips)和树旋转来调整,这样简单多了。】

    重点在(3),下面详细介绍(3)的细节:
    前提假设:设要插入的节点为N,其父节点为P,其父亲G的兄弟节点为U(即P和U是同一个节点的两个子节点)。
需要进一步学习的,请访问:
http://blog.chinaunix.net/uid-24774106-id-355157.html  红黑树插入背后的哲学
http://julyblog.blog.163.com/blog/static/18355431320113151014144/  红黑树从头至尾插入和删除结点的全程演示图
http://dongxicheng.org/structure/red-black-tree/  数据结构之红黑树

    插入节点时,会出现以下情形:
    情形1: 新节点N位于树的根上,没有父节点
    情形2: 新节点的父节点P是黑色
    情形3: 父节点P、叔叔节点U,都为红色,
    情形4: 父节点P是红色,叔叔节点U是黑色或NIL;
             插入节点N是其父节点P的右孩子,而父节点P又是其父节点的左孩子。
    情形5: 父节点P是红色,而叔父节点U 是黑色或NIL,
             要插入的节点N 是其父节点的左孩子,而父节点P又是其父G的左孩子。
   
    插入节点时,我们总是考虑插入节点N为红色;
    对于情形1,比较简单,将N节点重图为红色即可
    对于情形2,就更简单了,直接插入即可
    通常我们需要讨论的是情形3、4、5,以下详细介绍:
    对于情形3,如下图所示,我们将P和U重绘为黑色并重绘节点G为红色(用来保持性质5)。现在新节点N有了一个黑色的父节点P,因为通过父节点P或叔父节点U的任何路径都必定通过祖父节点G,在这些路径上的黑节点数目没有改变。但是,红色的祖父节点G的父节点也有可能是红色的,这就违反了性质4。为了解决这个问题,我们在祖父节点G上递归调整颜色。(重点记住,这是情形3,由于变换会产生情形3、4或者5,就可以递归用这3种情形,直至满足所有性质为止)
   
    对于情形4,如下图所示,我们对P进行一次左旋转调换新节点和其父节点的角色; 接着,按情形3来处理以前的父节点P以解决仍然失效的性质4。
   
    对于情形5,如下图所示,对祖父节点G 的一次右旋转; 在旋转产生的树中,以前的父节点P现在是新节点N和以前的祖父节点G 的父节点, 然后交换以前的父节点P和祖父节点G的颜色,结果的树满足性质4,同时性质5[4]也仍然保持满足。
   

    以上即是红黑树的插入操作,大家可以按照以上要求自行绘制一张图试试!
    分享一条经验,请把二叉树当成是一个链条,根结点处有一根钉子,当你拖拽链条的一端是,链条会如何?拽链条的目的就是为了保持链条两端平衡(即两边度之差的绝对值不>1)。再以此类推!
原文地址:https://www.cnblogs.com/p2liu/p/6048773.html