红黑树

1.红黑树的意义


  二叉查找树在极端的插入情况下,操作时间复杂度会变为O(n),但是*衡二叉树可以一直维持在O(lg(n))。因此*衡二叉查找树的效率很高,红黑树是一种自*衡二叉查找树的实现方式,这便是红黑树的意义。

2.红黑树性质


  [1]节点是红色或黑色。

  [2]根节点是黑色。

  [3]每个叶节点(NIL节点,空节点)是黑色的。

  [4]每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)

  [5]从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

3.变色、左旋和右旋


  变色的目的是进行操作后可以(更趋*于)保持红黑树的性质。

  左旋和右旋是变更节点在树中的位置,目的是进行这些操作后可以(更趋*于)保持红黑树的性质。

4.删除操作


  红黑树的删除和二叉查找树的删除操作是一样的,只是多了一个修复*衡的流程。  

  整体流程:首先按照二叉查找树的方式删除节点。之后判断释放节点的颜色,如果是红色,则结束,否则进入修复流程,修复流程就是一个状态机,直到走到完成状态。

  [1]按照二叉查找树的删除流程:

    1)如果删除节点的右子树为空,则把左节点接到删除节点位置。此时,删除节点就是释放节点。
    2)如果删除节点的左子树为空,则把右节点接到删除节点位置。此时,删除节点就是释放节点。
    3)如果左右都不为空,则把删除节点的左子树的最右节点(释放节点,也就是删除节点的后继节点)替换到要删除的节点,然后把释放节点的左子树接到其父节点上面。此时,后继节点就是释放节点。
  [2]如果释放节点的颜色是红色,则操作结束。否则,释放节点的颜色是黑色,并且释放节点只可能最多有一个子节点,把释放节点(删除节点或者后继节点)的子节点作为当前节点,释放节点的父节点作为父节点,进入状态机。

  [3]修复(状态机),循环执行以下操作:

    1)如果当前节点是红色,则把当前节点涂黑,操作结束。

    2)如果当前节点是黑色,且兄弟节点是红色,则进行操作:兄弟节点涂黑,父节点涂红,对父节点进行左旋。(此时,当前节点的父节点没变,但是兄弟节点变为黑色)。

    3)如果当前节点是黑色,且兄弟节点是黑色,且兄弟节点的两个子节点都是黑色,则进行操作:兄弟节点涂红,将父节点作为当前节点(附带的操作就是更新父节点为之前节点的祖父节点)。

    4)如果当前节点是黑色,且兄弟节点是黑色,且兄弟节点的右子节点是黑左子节点是红,则进行操作:兄弟节点涂红,兄弟左子节点涂黑,对兄弟节点进行右旋。(此时,当前节点的父节点没变,但是兄弟节点的右子节点变为红色)。

    5)如果当前节点是黑色,且兄弟节点是黑色,且兄弟节点的右子节点是红左子节点任意,则进行操作:兄弟节点的颜色涂成父节点的颜色,父节点涂黑,兄弟节点的右子节点涂黑,对父节点进行左旋,将根节点涂黑,操作结束。

    以上是当前节点是父节点左子节点的情况,如果是右子节点,只是做了对称处理(例如,对某个状态,作为左子节点时做左旋,而作为右子节点做右旋)。

5.插入操作


  红黑树的删除和二叉查找树的插入操作是一样的,只是多了一个修复*衡的流程。

  整体流程:首先按照二叉查找树的方式插入节点,把插入节点涂红。之后进入修复流程,修复流程就是一个状态机,直到走到完成状态。最后把根节点涂黑。

  [1]按照二叉查找树的插入流程:小于根的往左子树递归插入,否则往右子树递归插入。插入的节点必定是插到最下面那一层,不会插入到树的中间位置。

      初始把刚插入的节点涂红(涂红不会影响规则5,因此只需要处理更少的条件),把刚插入的节点作为当前节点。

  [2]修复(状态机),循环执行以下操作:

    1)如果当前节点的父节点是空,状态机完成。

    2)如果当前节点的父节点是黑色,状态机完成。

    3)如果当前节点的父节点是红色,叔叔节点是红色,则执行操作:将当前节点的父节点涂黑,叔叔节点涂黑,祖父节点涂红,再将祖父节点作为当前节点。

    4)如果当前节点的父节点是红色,叔叔节点是黑色,且当前节点是其父节点的右子节点,则执行操作:对当前节点的父节点执行左旋,并把当前节点的父节点作为当前节点(附带更新当前节点的父节点(就是之前的节点))。

    5)如果当前节点的父节点是红色,叔叔节点是黑色,且当前节点是其父节点的左子节点,则执行操作:对当前节点的父节点涂黑,祖父节点涂红,对祖父节点执行右旋。

    最后把根节点涂黑,结束。

    以上是当前节点是父节点左子节点的情况,如果是右子节点,只是做了对称处理(例如,对某个状态,作为左子节点时做左旋,而作为右子节点做右旋)。

6.总结


  [1]红黑树的插入和删除和二叉查找树的方式是一样的,之后多了一个修复流程,主要就是修复流程的状态机,对于某一个状态来说,需要进行一系列的操作,进行这个操作的目的是为了进入下一个状态,下一个状态肯定更趋*于树的*衡。

  [2]关于变色、左旋和右旋,这些操作的目的是为了让树的当前状态更趋于*衡,这个也是处理红黑树的关键,即什么时候需要变色、旋转,为何变色、旋转后可以更趋*于*衡,这个也需要在实践中熟练。

  [3]对于插入修复状态机的第4个状态说明:"对当前节点的父节点执行左旋",这个操作结束后,当前节点和父节点的位置会互换,因此把父节点作为当前节点后,当前节点的父节点就是之前的节点,代码就是:

register struct rb_node *tmp;
__rb_rotate_left(parent, root);        //对父节点左旋
tmp = parent;                          //
parent = node;                         //父节点作为当前节点,同时更新当前节点的父节点
node = tmp;                            //

  [4]代码,参照linux内核红黑树源码。

posted on 2019-03-04 14:03  能量星星  阅读(165)  评论(0编辑  收藏  举报

导航