紅黑樹插入算法實現原理分析

引言

紅黑樹是在實際工程中被普遍應用的一種數據結構,好比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

本文爲做者原創,轉載請於開頭明顯處聲明博客出處:)

相關文章
相關標籤/搜索