红黑树插入算法实现原理分析

引言

红黑树是在实际工程中被普遍应用的一种数据结构,好比Linux中的线程调度就是使用的红黑树来管理进程控制块,而Nginx中也是使用红黑树来管理的timer,Java中的TreeMap和TreeSet也是基于红黑树来实现的。红黑树相比普通二叉查找树的一个优点就是它的树高为~lgN,因此无论是查找/插入/删除操做它均能保证可以在对数时间以内完成。本文咱们就先来了解一下红黑树插入算法的实现。java

红黑树的定义

红黑树能够定义成含有红黑连接而且知足下列条件的二叉查找树:node

  1. 红连接均为左连接。
  2. 没有任何一个节点同时和两条红连接相连。
  3. 任意空连接到根节点的路径上的黑连接数目相同。p.s: 咱们将指向一棵空树的连接称为空连接。

好比下图就是一棵典型的红黑树,若是以前了解过2-3树的话(不了解也没有关系,咱们下面的内容并不会涉及到2-3树),能够发现若是将红黑树中的红色结点画平,实际上它就是2-3树的一种变形,不过相比2-3树,红黑树的查找操做要简单的多,但它同时也结合了2-3树中高效的插入操做,因此说红黑树实际上是普通的二叉查找树和2-3树两种数据结构优势的结合。算法

红黑树的定义

在实现红黑树以前咱们先来定义一棵红黑树:数据结构

public class RedBlackLiteBST<Key extends Comparable<Key>, Value> {

    private static final boolean RED   = true;
    private static final boolean BLACK = false;

    private Node root;     // root of the BST
    private int n;         // number of key-value pairs in BST

    // BST helper node data type
    private class Node {
        private Key key;           // key
        private Value val;         // associated data
        private Node left, right;  // links to left and right subtrees
        private boolean color;     // color of parent link

        public Node(Key key, Value val, boolean color) {
            this.key = key;
            this.val = val;
            this.color = color;
        }
    }
}

在上面的代码中,咱们将连接的颜色保存在表示该结点的Node对象中的color变量中。若是指向它的连接是红色的,那么该变量为true,黑色则为false,咱们规定空连接都为黑色。以下图所示,指向结点C的连接是红色,那么咱们就将h.left.color设置为红色,指向结点J的连接是黑色的,咱们就将h.right.color设置为黑色。this

红黑树的颜色表示

红黑树的几种基本操做

红黑树相比普通的二叉查找树的一个重要优点就是插入的高效性,可是正由于如此红黑树的插入操做的算法实现相比普通的二叉树要复杂一些。在正式实现插入算法以前,咱们有必要先了解一下对于红黑树的几种基本操做。spa

左旋转

以下图所示,如今咱们有一条红色的右连接,如今咱们想要将这条红色右连接转换为左连接,这个操做过程就叫作左旋转:线程

左旋转以前

咱们要作的就是在保持红黑树平衡性的同时,将上图的结构变为下面这样:code

左旋转以后

仔细观察能够发现,咱们要实现的其实就是将红色连接关联的两个节点中由较小的节点E做为根节点转换成由较大的节点S做为根节点。同时在这个过程当中为了保持二叉树中左子树都要小于根节点,右子树都要大于根节点,因此若是S节点还存在的话左连接咱们还须要将它变成E节点的右连接。具体的实现代码以下:对象

private Node rotateLeft(Node h) {
    assert (h != null) && isRed(h.right);
    Node x = h.right;
    h.right = x.left;
    x.left = h;
    x.color = h.color;
    h.color = RED;
    return x;
}

右旋转

右旋转的实现和左旋转的实现是相似的,就是将一个红色左连接转换成一个红色右连接:进程

右旋转以前

与左旋转的时候相反,咱们要作的其实就是红色连接关联的两个节点中较大的节点S做为根节点转换成由较小的节点E做为根节点:

右旋转以后

因此转换成具体的代码,咱们只须要将left和right相互转换就好了:

private Node rotateRight(Node h) {
    assert (h != null) && isRed(h.left);
    Node x = h.left;
    h.left = x.right;
    x.right = h;
    x.color = h.color;
    h.color = RED;
    return x;
}

颜色转换

上面咱们提到红黑树的一个重要特性就是红连接均为作左连接,因此对于下面这种情形,若是一个结点的两个子结点均为红色连接,咱们就将子结点的颜色所有由红色转换成黑色,同时将父结点的颜色由黑变红。

颜色转换以前

颜色转换以后

具体的实现代码很是简单:

private void flipColors(Node h) {
    assert !isRed(h) && isRed(h.left) && isRed(h.right);
    h.color = RED;
    h.left.color = BLACK;
    h.right.color = BLACK;
}

插入操做的实现

上面说了这么多,其实都是为接下来的插入操做作的预热。接下来结合下图,咱们来分析红黑树插入操做过程咱们会遇到的三种情形:

  1. 插入的新结点c大于树中现存的两个键,因此咱们要将它链接到b结点的右连接。由于这个时候b的两条连接都是红连接,因此咱们要进行flipColors。接下来能够发现,咱们下面的两种状况都会转换成这种情形。
  2. 插入的新结点a要比树中现存的两个键都要小,因此咱们先将它链接到结点b的左连接,前面咱们提到红黑树的一个特性就是没有任何一个节点能够同时和两个红色连接相连,而如今b结点却违背了这一原则,因此咱们要进行右旋转操做,接下来情形1是如出一辙的了。
  3. 插入的新结点b在树中现存的两个键之间,因此咱们先将它链接到结点a的右连接,前面咱们提到红黑树中红色连接都是左连接,因此咱们首先要进行左旋转操做,接下来就和情形2如出一辙了。

插入操做的3种情形

插入操做的具体实现代码以下,下面的代码直到// fix-up any right-leaning right以前咱们作的都是为了找到合适的插入位置,而以后的3个if语句实际上就是对上图中情形3的一种总结。

public void put(Key key, Value val) {
    root = insert(root, key, val);
    root.color = BLACK;
    // assert check(); // Check integrity of red-black tree data structure.
}

private Node insert(Node h, Key key, Value val) {
    if (h == null) {
        n++;
        return new Node(key, val, RED);
    }

    int cmp = key.compareTo(h.key);
    if      (cmp < 0) h.left  = insert(h.left,  key, val);
    else if (cmp > 0) h.right = insert(h.right, key, val);
    else              h.val   = val;

    // fix-up any right-leaning links
    if (isRed(h.right) && !isRed(h.left))      h = rotateLeft(h);
    if (isRed(h.left)  &&  isRed(h.left.left)) h = rotateRight(h);
    if (isRed(h.left)  &&  isRed(h.right))     flipColors(h);

    return h;
}

isRed的实现很是简单,我就不解释了:

// is node x red (and non-null) ?
private boolean isRed(Node x) {
    if (x == null) return false;
    return x.color == RED;
}

References

ALGORITHM 4TH

本文为做者原创,转载请于开头明显处声明博客出处:)

相关文章
相关标签/搜索