紅黑樹-想說愛你不容易

前言:java

  記得在大一懵懵懂懂的時候就接觸了紅黑樹的算法。但因爲當時內功尚淺,沒法將其內化,只是以爲它很神奇,是個好算法,設計它的人很牛!現今重拾起這個算法,不得再也不次被它的精妙所折服!編寫本文,是但願以鄙人的理解將紅黑樹算法的精髓向博客園的園友陳述一番,也但願對其有獨特看法的朋友能不吝賜教。準備好了的話,咱們就開始吧~算法

--------------------------------------------this

Part I:BSTspa

  做爲開始,咱們得先談談二叉樹(Binary Search Tree)。設計

1.假設存在一個以下簡單的鍵值字符表:code

Key Valueblog

A 2遞歸

C 1ip

B 6get

B 11

H 1

J 3

  要求你按照讀入順序創建這樣一棵二叉查找樹,建好以後要求可以進行對於的查詢操做。

源於二分查找的思想,二叉查找樹有這樣一個特色:

  對於樹上的任意一個結點,若是它有左右子結點的話,其結點大小一定大於其左子結點且小於其右子結點。

 

2.查找get(key)

 

因爲單獨創建一個二叉查找樹起初很差分析,咱們就假設如今有一棵已經構造好二叉查找樹。咱們僅須要思考如何在其上面進行查找操做。

根據二分查找的思想,咱們能夠按照下面步驟進行查找:

Step1:將須要查找的key與二叉查找樹的當前根節點的key做比較,獲得比較結果後進行下面的step2;

Step2:若查找的key比根節點的key小,則遞歸從根節點的左子樹進行一樣的查找key操做;若比根節點的key大,則遞歸地從根節點的右子樹進行一樣的查找key操做;

若,查找的key恰好等於當前根節點的key,則返回當前key對應的value,結束!

 

3.插入put(key,value)

假設如今已經有了一個二叉查找樹,咱們要插入一對鍵值(key-value)。源於查找過程的經驗,咱們知道插入操做其實近似於查找操做。由於,咱們插入的時候一樣是拿key跟當前根節點的key比較,以後再肯定是往左走仍是右走,或者是更新當前值(key=root.key時)。

 

Code:

 1 package com.gdufe.binarysearchtree;
 2 
 3 import java.io.File;
 4 import java.util.Scanner;
 5 
 6 public class BST<Key extends Comparable<Key>, Value> {
 7 
 8     Node root; // 維護根節點
 9 
10     class Node { // 二叉樹的結點
11         Key key;
12         Value value;
13         Node left, right;
14 
15         public Node(Key key, Value value) { // 初始化結點
16             this.key = key;
17             this.value = value;
18         }
19     }
20 
21     public Value get(Key key) {
22         return get(root, key);
23     }
24 
25     //查找操做
26     public Value get(Node x, Key key) {
27         if (x == null)
28             return null;
29         int cmp = key.compareTo(x.key);
30         if (cmp < 0)
31             return get(x.left, key);
32         else if (cmp > 0)
33             return get(x.right, key);
34         else
35             return x.value;
36     }
37 
38     public void put(Key key, Value value) {
39         root = put(root, key, value);
40     }
41     //插入操做
42     public Node put(Node x, Key key, Value value) {
43         if (x == null)
44             return new Node(key, value);
45         int cmp = key.compareTo(x.key);
46         if (cmp < 0)
47             x.left = put(x.left, key, value);
48         else if (cmp > 0)
49             x.right = put(x.right, key, value);
50         else
51             x.value = value;
52         return x;
53     }
54 
55     public static void main(String[] args) throws Exception {
56         Scanner input = new Scanner(new File("data_BST.txt"));
57         BST<String, Integer> bst = new BST<String, Integer>();
58         while (input.hasNext()) {
59             String key = input.next();
60             int value = input.nextInt();
61             bst.put(key, value);
62         }
63         System.out.println(bst.get("H"));
64         System.out.println(bst.get("B"));
65     }
66 
67 }

 

輸出結果:

1
11

 

 分析:

  插入或查找時,有可能最壞狀況樹不斷惡意生長(垂直生長),此時的時間複雜度爲:ON,平均的時間複雜度爲:O(lgN)

----------------------------------------

Part II:RedBlackBST

1. 2-3

在二叉樹的基礎之上,咱們引入了平衡2-3樹。簡單地說,二叉樹每一個結點至多隻能有2個子結點(稱爲「2結點」),而如今咱們能夠經過將2個結點「綁」在一塊兒造成一個有3個子結點的「3結點」。見下圖:

 

因爲查找操做較簡單,咱們重點討論它的插入操做。一樣基於上面所給的數據,見圖:

 ------------------------------------------------

 2.紅黑二叉查找樹(簡稱「紅黑樹」)

  那麼問題來了,咱們該如何實現這樣一棵2-3樹呢?正常的思惟固然是但願在原先的Node結構中進行重構,再構造一個嵌套的BIGNode。但巧妙的地方就在這裏,咱們能夠以以前的二叉查找樹爲基礎,把結點之間的連接分爲「紅連接」和「黑連接」。其中,紅鏈接經過鏈接兩個2結點組成3結點,黑鏈接是以前二叉查找樹的普通鏈接。爲了方便,咱們不妨把3結點統一表示爲一條左斜的紅色連接。如圖:

 

  上面經過定義紅黑樹的規則實現咱們等價的2-3樹結構,因而紅黑樹也就有了下面等價的定義。

含有紅黑連接而且知足下列條件的二叉查找樹:

1)紅連接均爲左連接

2)沒有任何結點同時和2條紅連接相連

3)任意空連接到根節點路徑上的黑連接數相同

---------------------------------------------

既然從上面的闡述中,咱們得出 了「紅黑樹≈2-3樹",咱們咱們緊接着用上面的數據構建咱們的紅黑樹,見圖:

 

  其中,存在着3個關鍵操做:

左旋:當結點出現左子結點爲黑,右子結點爲紅時,進行左旋轉;

右旋:當結點出現左子結點以及左子結點的左結點均爲紅時,進行右旋轉;

變色:當結點出現左右子結點均爲紅時,進行變色操做(2個子連接均變黑色,並將紅連接向上傳遞!)

 具體,見下圖:

Code:

  1 package com.gdufe.binarysearchtree;
  2 
  3 import java.io.File;
  4 import java.util.Scanner;
  5 
  6 public class RedBlackTree<Key extends Comparable<Key>, Value> {
  7 
  8     Node root; // 維護根節點
  9 
 10     final static boolean RED = true;
 11     final static boolean BLACK = false;
 12 
 13     class Node { // 二叉樹的結點
 14         Key key;
 15         Value value;
 16         boolean color;
 17         Node left, right;
 18 
 19         public Node(Key key, Value value, boolean color) { // 初始化結點
 20             this.key = key;
 21             this.value = value;
 22             this.color = color;
 23         }
 24     }
 25 
 26     public Value get(Key key) {
 27         return get(root, key);
 28     }
 29 
 30     // 右旋
 31     public Node rotateRight(Node h) {
 32         Node x = h.left;
 33         h.left = x.right;
 34         x.right = h;
 35         x.color = h.color;
 36         h.color = RED;
 37         return x;
 38     }
 39 
 40     // 左旋
 41     public Node rotateLeft(Node h) {
 42         Node x = h.right;
 43         h.right = x.left;
 44         x.left = h;
 45         x.color = h.color;
 46         h.color = RED;
 47         return x;
 48     }
 49 
 50     // 變色處理
 51     public void flipColors(Node h) {
 52         h.left.color = BLACK;
 53         h.right.color = BLACK;
 54         h.color = RED;
 55     }
 56     public boolean isRed(Node x){
 57         if(x==null) return false;
 58         else return x.color;
 59     }
 60     public Value get(Node x, Key key) {
 61         if (x == null)
 62             return null;
 63         int cmp = key.compareTo(x.key);
 64         if (cmp < 0)
 65             return get(x.left, key);
 66         else if (cmp > 0)
 67             return get(x.right, key);
 68         else
 69             return x.value;
 70     }
 71 
 72     public void put(Key key, Value value) {
 73         root = put(root, key, value);
 74         root.color = BLACK;
 75     }
 76 
 77     public Node put(Node x, Key key, Value value) {
 78         if (x == null)
 79             return new Node(key, value, RED); // 添加的結點連接爲紅色
 80         int cmp = key.compareTo(x.key);
 81         if (cmp < 0)
 82             x.left = put(x.left, key, value);
 83         else if (cmp > 0)
 84             x.right = put(x.right, key, value);
 85         else {
 86             x.value = value;
 87         }
 88         // 判斷是否須要左旋,右旋,變色操做
 89         if (x != null) {
 90             if (!isRed(x.left) && isRed(x.right))
 91                 x = rotateLeft(x); 
 92             if (isRed(x.left) && isRed(x.left.left))
 93                 x = rotateRight(x);
 94             if (isRed(x.left ) && isRed(x.right))
 95                 flipColors(x);
 96         }
 97 
 98         return x;
 99     }
100 
101     public static void main(String[] args) throws Exception {
102         Scanner input = new Scanner(new File("data_BST.txt"));
103         RedBlackTree<String, Integer> bst = new RedBlackTree<String, Integer>();
104         while (input.hasNext()) {
105             String key = input.next();
106             int value = input.nextInt();
107             bst.put(key, value);
108         }
109         System.out.println(bst.get("H"));
110         System.out.println(bst.get("B"));
111     }
112 
113 }

輸出結果:

1
11

分析:

  有了上面3個關鍵操做以後,咱們保證了樹的平衡性,即樹不會再惡意生長。插入N個結點後,樹的高度爲:O(lgN)~O(2*lgN) (思考一下?)。因此,咱們獲得插入和查找的總體時間複雜度均降爲:O(lgN)。

--------------------------

結語:

不得不認可,紅黑樹算法堪稱算法研究領域的非凡之做。在現今的汪洋信息時代,存在着上億的數據。可是,當咱們用紅黑樹算法對其進行動態的增長和查找時,僅僅須要幾十次操做便可完事兒,怎能不讓人拍案叫絕!!

相關文章
相關標籤/搜索