彻底理解红黑树( 二 )

  • 若当前结点为空,返回null;
  • 若当前结点不为空,用当前结点的key跟查找key作比较;
  • 若当前结点key等于查找key,那么该key就是查找目标,返回当前结点;
  • 若当前结点key大于查找key,把当前结点的左子结点设置为当前结点,重复步骤2;
  • 若当前结点key小于查找key,把当前结点的右子结点设置为当前结点,重复步骤2;
  • 如图5所示:
    彻底理解红黑树

    文章插图
     
    图5 二叉树查找流程图
    非常简单,但简单不代表它效率不好 。正由于红黑树总保持黑色完美平衡,所以它的查找最坏时间复杂度为O(2lgN),也即整颗树刚好红黑相隔的时候 。能有这么好的查找效率得益于红黑树自平衡的特性,而这背后的付出,红黑树的插入操作功不可没~
    三、红黑树插入
    插入操作包括两部分工作:一查找插入的位置;二插入后自平衡 。查找插入的父结点很简单,跟查找操作区别不大:
    1. 从根结点开始查找;
    2. 若根结点为空,那么插入结点作为根结点,结束 。
    3. 若根结点不为空,那么把根结点作为当前结点;
    4. 若当前结点为null,返回当前结点的父结点,结束 。
    5. 若当前结点key等于查找key,那么该key所在结点就是插入结点,更新结点的值,结束 。
    6. 若当前结点key大于查找key,把当前结点的左子结点设置为当前结点,重复步骤4;
    7. 若当前结点key小于查找key,把当前结点的右子结点设置为当前结点,重复步骤4;
    如图6所示:
    彻底理解红黑树

    文章插图
     
    图6 红黑树插入位置查找
     
    ok,插入位置已经找到,把插入结点放到正确的位置就可以啦,但插入结点是应该是什么颜色呢?答案是红色 。理由很简单,红色在父结点(如果存在)为黑色结点时,红黑树的黑色平衡没被破坏,不需要做自平衡操作 。但如果插入结点是黑色,那么插入位置所在的子树黑色结点总是多1,必须做自平衡 。
    所有插入情景如图7所示:
    彻底理解红黑树

    文章插图
     
    图7 红黑树插入情景
    嗯,插入情景很多呢,8种插入情景!但情景1、2和3的处理很简单,而情景4.2和情景4.3只是方向反转而已,懂得了一种情景就能推出另外一种情景,所以总体来看,并不复杂,后续我们将一个一个情景来看,把它彻底搞懂 。
    另外,根据二叉树的性质,除了情景2,所有插入操作都是在叶子结点进行的 。这点应该不难理解,因为查找插入位置时,我们就是在找子结点为空的父结点的 。
    在开始每个情景的讲解前,我们还是先来约定下,如图8所示:
    彻底理解红黑树

    文章插图
     
    图8 插入操作结点的叫法约定
    图8的字母并不代表结点Key的大小 。I表示插入结点,P表示插入结点的父结点,S表示插入结点的叔叔结点,PP表示插入结点的祖父结点 。
    好了,下面让我们一个一个来分析每个插入的情景以其处理 。
    插入情景1:红黑树为空树最简单的一种情景,直接把插入结点作为根结点就行,但注意,根据红黑树性质2:根节点是黑色 。还需要把插入结点设为黑色 。
    处理:把插入结点作为根结点,并把结点设置为黑色 。
    插入情景2:插入结点的Key已存在插入结点的Key已存在,既然红黑树总保持平衡,在插入前红黑树已经是平衡的,那么把插入结点设置为将要替代结点的颜色,再把结点的值更新就完成插入 。
    处理:
    • 把I设为当前结点的颜色
    • 更新当前结点的值为插入结点的值
    插入情景3:插入结点的父结点为黑结点由于插入的结点是红色的,当插入结点的黑色时,并不会影响红黑树的平衡,直接插入即可,无需做自平衡 。
    处理:直接插入 。
    插入情景4:插入结点的父结点为红结点再次回想下红黑树的性质2:根结点是黑色 。如果插入的父结点为红结点,那么该父结点不可能为根结点,所以插入结点总是存在祖父结点 。这点很重要,因为后续的旋转操作肯定需要祖父结点的参与 。
    情景4又分为很多子情景,下面将进入重点部分,各位看官请留神了 。
    插入情景4.1:叔叔结点存在并且为红结点
    从红黑树性质4可以,祖父结点肯定为黑结点,因为不可以同时存在两个相连的红结点 。那么此时该插入子树的红黑层数的情况是:黑红红 。显然最简单的处理方式是把其改为:红黑红 。如图9和图10所示 。


    推荐阅读