從二叉搜索樹到AVL樹再到紅黑樹 B樹

這幾種樹都屬於數據結構中較爲複雜的,在平時面試中,常常會問理解用法,但通常不會問具體的實現,因此今天來梳理一下這幾種樹之間的區別與聯繫,感謝知乎用戶@Cailiang,這篇文章參考了他的專欄。node

二叉查找樹

是一棵空樹,或是具備下列性質的二叉樹:面試

若它的左子樹不空,則左子樹上全部結點的值均小於它的根結點的值; 若它的右子樹不空,則右子樹上全部結點的值均大於它的根結點的值;它的左、右子樹也分別爲二叉排序樹。數據庫

插入數據:

1 若是根節點爲空,則將插入的節點做爲根節點數據結構

2 不然和根節點比較(咱們是經過key來比較,因此 K 必須是實現了Comparable接口的對象)若是比根節點小,則新節點會插入到左子樹中,若是比根節點大,則新節點會插入到右子樹中,若是和根節點相等,則更新根節點的值ide

經過key查詢:

1 若是根節點爲空,返回 null性能

2 若是,key 和根節點的key相同,則返回根節點this

3 若是,key比根節點的key小,則遞歸查找根節點的左節點spa

4 若是,key比根節點的key大,則遞歸查找根節點的右節點指針

刪除節點:

1 將要刪除的節點沒有子節點 ----> 直接刪除code

2 將要刪除的節點下有一個子節點 -----> 將要被刪除的節點的子節點掛靠到將要被刪除的節點的父節點上便可

3 將要刪除的節點下有兩個子節點 ----> 在將要被刪除的節點的右子樹中找到一個最小節點,而後用找到的最小節點與須要刪除的節點替換。最後再將最小節點進行刪除。(這裏你可能有其它的方案,咱們這裏這麼作的緣由是一,右子樹的最小節點必定沒有左節點,處理起來會簡單一些,二,這樣能夠保持樹的高度不變,甚至是下降樹的高度,樹的高度越低意味着操做的時間複雜度越低。因此實現刪除一個節點的原則是,用最小的代價實現刪除的功能,而且保持樹的高度不變甚至下降樹的高度)

 

 

搜索二叉樹的增刪改查的性能都不錯,可是在一些特殊狀況下,搜索二叉樹的性能可能由對數型變成線性,性能大大下降,好比插入的一組數據是有序的,那麼二叉搜索樹的結構將變成一個鏈表時間複雜度變爲 O(N),也就是說插入一組有序或局部有序的數據將會致使二叉搜索樹不平衡,樹的高度會很大,時間複雜度會趨近於 O(N)。

 

爲了解決這個問題,引出了平衡二叉樹(AVL)。

平衡二叉樹,首先是一棵二叉查找樹,可是它知足一點重要的特性:每個結點的左子樹和右子樹的高度差最多爲1。這個高度差限制就徹底規避了上述的最壞狀況,所以查找、插入和刪除的時間複雜度都變成了O(lg n)。

 

AVL樹雖然是一個完美平衡的二叉排序樹,而且保證插入,刪除,查找的時間複雜度爲 lg n, 可是AVL樹的應用卻不普遍,緣由就是維護一顆AVL樹操做太複雜,成本過高。當咱們對一顆AVL樹進行插入或刪除的操做時,咱們須要不斷的回朔來修改平衡因子(左子樹的高度減去右字樹的高度),須要經過左旋轉,右旋轉來保證每一個節點的左右子樹的高度差不超過1。這些複雜的操做甚至抵消了AVL樹給咱們帶來的性能上的提高,因此咱們通常不會使用AVL樹。

 

紅黑樹

吸收了AVL樹和2-3樹的思想,但放棄了AVL樹完美平衡的特性,改成局部平衡或完美黑色平衡;放棄了2-3樹3節點,改成經過顏色(紅色和黑色)來區分不一樣的節點類型,這樣就下降了維護平衡的成本和實現的複雜度,能夠直接使用二叉排序樹的查詢方法來查詢無需任何修改。

恢復紅黑屬性須要少許(O(log n))的顏色變動(這在實踐中是很是快速的)而且不超過三次樹旋轉(對於插入是兩次)。插入和刪除爲 O(log n) 次,可是這致使了很是複雜的操做。

紅黑樹的性質:

1 全部節點都是紅色或者黑色

2 根節點爲黑色

3 全部的 NULL 葉子節點都是黑色

4 若是該節點是紅色的,那麼該節點的子節點必定都是黑色

5 全部的 NULL 節點到根節點的路徑上的黑色節點數量必定是相同的

 

 

B-Tree

前面介紹了AVL樹,紅黑樹,它們都屬於二叉樹,即每一個節點最多隻能擁有2個子節點,而B-tree(B樹)的每一個節點能夠擁有2個以上的子節點,因此簡單歸納一下:B-tree就是一顆多路平衡查找樹,它普遍應用於數據庫索引和文件系統中。

首先咱們介紹下 m 階B-tree的特性,那麼這個 m 階是怎麼定義的呢?這裏咱們以一個節點能擁有的最大子節點數來表示這顆樹的階數。舉個例子,若是一個節點最多有 n 個key,那麼這個節點最多就會有 n+1 個子節點,這棵樹就叫作 n+1(m=n+1)階樹。一顆 m 階B-tree包括如下5條特性:

 

1 每一個節點最多有 m 個子節點

2 除根節點和葉子節點,其它每一個節點至少有 [m/2] (向上取整的意思)個子節點

3 若根節點不是葉子節點,則其至少有2個子節點

4 全部NULL節點到根節點的高度都同樣

5 除根節點外,其它節點都包含 n 個key,其中 [m/2] -1 <= n <= m-1

 

B+ Tree是B Tree的一個變種,不一樣之處在於:

第一:在 B-Tree中一個含有n個子樹的節點有n-1個關鍵字(key)。而在 B+Tree中一個含有n個子樹的節點有n個關鍵字(key)。

爲何在擁有一樣子樹的狀況下B+Tree的節點多須要一個key呢?那是由於 B+Tree的節點會存儲該節點的子樹中最小的key。

第二:B-Tree的每一個節點都包含了關鍵字(key)以及指向包含這些關鍵字記錄的指針。而 B+Tree非葉子節點僅用來索引,數據都保存在葉子節點中。

在葉子節點中存儲了全部的關鍵字信息,以及指向包含這些關鍵字記錄的指針。並且這些葉子節點構成一個有序鏈表,即每一個葉子節點會有一個指針指向其兄弟節點。在非葉子節點中只存儲了關鍵字信息。

下面這張圖畫的很是好:

 

 

 

在數據庫索引的實現中,大部分採用的是B+Tree而不是B-Tree,這又是爲何呢?

緣由有二,其一是因爲B+Tree 在非葉子節點中只存儲了關鍵字信息,而沒有存儲指向包含這些關鍵字記錄的指針,因此在樹的高度相同時,B+Tree每每能比B-Tree存儲更多的關鍵字信息。更最要的緣由是由於 B+Tree在葉子節點中存儲了全部的關鍵字信息,以及指向包含這些關鍵字記錄的指針。並且這些葉子節點構成一個有序鏈表,這樣B+Tree在實現範圍查詢的時候就比較容易,只須要遍歷這個有序鏈表就行。而B-tree要實現範圍查詢則比較困難,但範圍查詢又是數據庫中比較經常使用的功能,因此數據庫中大部分採用的是B+Tree而不是B-Tree。固然B-Tree也有強於B+tree的地方,例如在隨機查詢中,因爲B-Tree的每一個節點都包含了關鍵字(key)以及指向包含這些關鍵字記錄的指針,因此B-Tree可能中途就查詢到須要的數據,不須要遍歷到葉子節點。而B+Tree因爲只在葉子節點中存儲了全部的關鍵字信息,以及指向包含這些關鍵字記錄的指針。在非葉子節點中只存儲了關鍵字信息,沒有存儲指向包含這些關鍵字記錄的指針,因此B+Tree必定要遍歷到葉子節點才能獲取到包含這些關鍵字記錄的指針。因此B-Tree的隨機查詢性能會高於B+Tree。

 

在B+樹的基礎上又演變出B*樹,B*Tree在非葉子結點中也增長了指向兄弟節點的指針,而且它將非葉子節點上存儲的關鍵字個數的最小值提升到 (2/3) * m,這樣的話就提升了空間利用率。

  1 public class BinarySerachTree<K extends Comparable<K>, V> {
  2 
  3     private class Node {
  4         private K key; // 存儲的key
  5         private V value; // 存儲的值
  6         private Node leftNode; // 左節點
  7         private Node rightNode; // 右節點
  8 
  9         public Node(K key, V value, Node leftNode, Node rightNode) {
 10             super();
 11             this.key = key;
 12             this.value = value;
 13             this.leftNode = leftNode;
 14             this.rightNode = rightNode;
 15         }
 16 
 17         @Override
 18         public String toString() {
 19             return "{\"key\":" + this.key + ", \"value\":" + "\"" + this.value + "\"" + ", \"leftNode\":"
 20                     + this.leftNode + ", \"rightNode\":" + this.rightNode + "}";
 21         }
 22     }
 23 
 24     private Node root; // 根節點
 25 
 26     public void put(K key, V value) {
 27         Node newNode = new Node(key, value, null, null);
 28         if (null == root) {
 29             root = newNode;
 30         } else {
 31             upsert(null, root, newNode);
 32         }
 33     }
 34 
 35     private void upsert(Node parent, Node current, Node newNode) {
 36         if (null == current) {
 37             if (newNode.key.compareTo(parent.key) > 0) {
 38                 parent.rightNode = newNode;
 39             } else {
 40                 parent.leftNode = newNode;
 41             }
 42         } else {
 43             int result = newNode.key.compareTo(current.key);
 44             if (result == 0) {
 45                 current.value = newNode.value;
 46             }
 47             parent = current;
 48             if (result > 0) {
 49                 upsert(parent, parent.rightNode, newNode);
 50             }
 51             if (result < 0) {
 52                 upsert(parent, parent.leftNode, newNode);
 53             }
 54         }
 55     }
 56 
 57     public Node get(K key) {
 58         if (null != key) {
 59             return find(key, root); // 從根節點開始找
 60         }
 61         return null;
 62     }
 63 
 64     private Node find(K key, Node root) {
 65         if (null != root) {
 66             int result = key.compareTo(root.key);
 67             if (result == 0) {
 68                 return root;
 69             }
 70             if (result > 0) {
 71                 return find(key, root.rightNode);
 72             }
 73             if (result < 0) {
 74                 return find(key, root.leftNode);
 75             }
 76         }
 77         return null;
 78     }
 79 
 80     public boolean delete(K key) {
 81         if (null != key) {
 82             if (null != root) {
 83                 return deleteNode(key, root, null);
 84             }
 85         }
 86         return false;
 87     }
 88 
 89     private boolean deleteNode(K key, Node current, Node parent) {
 90         if (null != current) {
 91             if (key.compareTo(current.key) > 0) {
 92                 return deleteNode(key, current.rightNode, current);
 93             }
 94             if (key.compareTo(current.key) < 0) {
 95                 return deleteNode(key, current.leftNode, current);
 96             }
 97             if (key.compareTo(current.key) == 0) {
 98                 if ((null != current.leftNode) && (null != current.rightNode)) { // 將要刪除的節點下有兩個子節點
 99                     dleTwoChildrenNode(current);
100                     return true;
101                 } else {
102                     if ((null == current.leftNode) && (null == current.rightNode)) { // 將要刪除的節點沒有子節點
103                         if (current.key.compareTo(parent.key) > 0) {
104                             parent.rightNode = null;
105                         } else {
106                             parent.leftNode = null;
107                         }
108                         return true;
109                     } else { // 將要被刪除的節點的子節點掛靠到將要被刪除的節點的父節點上便可
110                         Node childNode = (null == current.leftNode) ? current.rightNode : current.leftNode;
111                         if (current.key.compareTo(parent.key) > 0) {
112                             parent.rightNode = childNode;
113                         } else {
114                             parent.leftNode = childNode;
115                         }
116                         return true;
117                     }
118                 }
119             }
120         }
121         return false;
122     }
123 
124     /**
125      * 處理被刪除節點有兩個子節點的狀況
126      * @param parent
127      * 將要被刪除的節點
128      */
129     private void dleTwoChildrenNode(Node parent) {
130         Node parentRight = parent.rightNode;
131         Node tmp = parentRight.leftNode;
132         if (null == tmp) {
133             parent.value = parentRight.value;
134             parent.key = parentRight.key;
135             parent.rightNode = parentRight.rightNode;
136         } else {
137             Node tmpParent = parentRight;
138             while (null != tmp.leftNode) {
139                 tmpParent = tmp;
140                 tmp = tmp.leftNode;
141             }
142             parent.value = tmp.value;
143             parent.key = tmp.key;
144             tmpParent.leftNode = tmp.rightNode;
145         }
146     }
147 
148     public static void main(String[] args) {
149 
150         BinarySerachTree<Integer, String> bst = new BinarySerachTree<Integer, String>();
151 
152         bst.put(100, "v100");
153         bst.put(50, "v50");
154         bst.put(150, "v150");
155         bst.put(20, "v20");
156         bst.put(85, "v85");
157         bst.put(10, "v10");
158         bst.put(15, "a15");
159         bst.put(75, "v75");
160         bst.put(95, "v95");
161         bst.put(65, "v65");
162         bst.put(76, "v76");
163         bst.put(60, "v60");
164         bst.put(66, "v66");
165         bst.put(61, "v61");
166 
167         System.out.println(bst.get(100));// 打印根節點
168     }
169 }
BinarySerachTree
  1 public class RedBlackTree<K extends Comparable<K>, V> {
  2 
  3     private static final byte RED = 1;
  4     private static final byte BLACK = 0;
  5 
  6     private class Node {
  7         private byte color;
  8         private K key; // 存儲的key
  9         private V value; // 存儲的值
 10         private Node leftNode; // 左節點
 11         private Node rightNode; // 右節點
 12         private Node parentNode; // 父節點
 13 
 14         public Node(K key, V value, Node leftNode, Node rightNode, byte color, Node parentNode) {
 15             super();
 16             this.key = key;
 17             this.value = value;
 18             this.leftNode = leftNode;
 19             this.rightNode = rightNode;
 20             this.color = color;
 21             this.parentNode = parentNode;
 22         }
 23 
 24         @Override
 25         public String toString() {
 26             return "{" + "\"key\":" + this.key + ", " + "\"value\":" + "\"" + this.value + "\"" + ", " + "\"color\":"
 27                     + this.color + ", " + "\"leftNode\":" + this.leftNode + "," + "\"rightNode\":" + this.rightNode
 28                     + "}";
 29         }
 30     }
 31 
 32     private Node root; // 根節點
 33 
 34     public Node getRoot() {
 35         return this.root;
 36     }
 37 
 38     private void leftRotation(Node h) {
 39         Node m = h.rightNode;
 40         // 1. 將 k 節點設置爲 h 的右節點
 41         h.rightNode = m.leftNode;
 42         if (null != m.leftNode) {
 43             m.leftNode.parentNode = h;
 44         }
 45         // 2. 將 h 的父節點 賦給 m 的父節點,以後分 3 種狀況討論
 46         m.parentNode = h.parentNode;
 47         if (null == m.parentNode) { // I: 說明 h 原來是根節點,如今將 m 設置爲新的根節點
 48             root = m;
 49         } else {
 50             if (h.key.compareTo(h.parentNode.key) < 0) { // II: 說明 h 原來是它父節點的左孩子,如今將 m 設置爲新的左孩子
 51                 h.parentNode.leftNode = m;
 52             } else {
 53                 h.parentNode.rightNode = m; // III: 說明 h 原來是它父節點的右孩子,如今將 m 設置爲新的右孩子
 54             }
 55         }
 56         // 3. 將 h 掛靠在 m 的左孩子上
 57         m.leftNode = h;
 58         h.parentNode = m;
 59     }
 60 
 61     private void rightRotation(Node m) {
 62         Node h = m.leftNode;
 63         // 1. 將 k 設置爲 m 的左節點
 64         m.leftNode = h.rightNode;
 65         if (null != h.rightNode) {
 66             h.rightNode.parentNode = m;
 67         }
 68         // 2. 將 m 的父節點 賦給 h 的父節點,以後分 3 種狀況討論
 69         h.parentNode = m.parentNode;
 70         if (null == m.parentNode) { // I: 說明 m 原來是根節點,如今將 h 設置爲新的根節點
 71             root = h;
 72         } else {
 73             if (m.key.compareTo(m.parentNode.key) < 0) { // II: 說明 m 原來是它父節點的左孩子,如今將 h 設置爲新的左孩子
 74                 m.parentNode.leftNode = h;
 75             } else {
 76                 m.parentNode.rightNode = h; // III: 說明 m 原來是它父節點的右孩子,如今將 h 設置爲新的右孩子
 77             }
 78         }
 79         // 3. 將 m 掛靠在 h 的右孩子上
 80         h.rightNode = m;
 81         m.parentNode = h;
 82     }
 83 
 84     /**
 85      * 插入新的節點,若是指定的key已經存在,則更新原來的值
 86      * 
 87      * @param key
 88      * @param value
 89      */
 90     public void put(K key, V value) {
 91         Node newNode = new Node(key, value, null, null, RED, null);
 92         if (null == root) {
 93             root = newNode;
 94             root.color = BLACK;
 95         } else {
 96             upsert(null, root, newNode);
 97         }
 98     }
 99 
100     private void upsert(Node parent, Node current, Node newNode) {
101         if (null == current) {
102             if (newNode.key.compareTo(parent.key) > 0) {
103                 parent.rightNode = newNode;
104             } else {
105                 parent.leftNode = newNode;
106             }
107             newNode.parentNode = parent;
108             upsertFix(newNode); // 插入新節點後 對紅黑樹進行修復
109         } else {
110             int result = newNode.key.compareTo(current.key);
111             if (result == 0) {
112                 current.value = newNode.value;
113             }
114             parent = current;
115             if (result > 0) {
116                 upsert(parent, parent.rightNode, newNode);
117             }
118             if (result < 0) {
119                 upsert(parent, parent.leftNode, newNode);
120             }
121         }
122     }
123 
124     private void upsertFix(Node newNode) {
125         Node parent = newNode.parentNode;
126         if (RED == parent.color) { // 父節點若是是黑節點 則不須要處理
127             Node grandfather = parent.parentNode;
128             if (parent == grandfather.leftNode) { // 1. 父節點原來是 左節點
129                 Node uncle = grandfather.rightNode;
130                 if ((null != uncle) && (RED == uncle.color)) { // case 3: 叔叔節點是紅色
131                     uncleRedFix(newNode);
132                 } else { // 叔叔節點爲 NULL 或者 是黑色節點
133                     if (newNode.key.compareTo(parent.key) < 0) { // case 1: 叔叔節點是黑色,插入到左子樹中
134                         leftNodeFix(grandfather, parent);
135                     } else { // case 2: 叔叔節點是黑色,插入到右子樹中
136                         leftRotation(parent);
137                         leftNodeFix(grandfather, newNode); // 咱們將 parent 節點做爲「新插入的節點」,這樣 真正新插入的節點 newNode 就是父節點
138                     }
139                 }
140             } else { // 1. 父節點原來是 右節點
141                 Node uncle = grandfather.leftNode;
142                 if ((null != uncle) && (RED == uncle.color)) { // case 3: 叔叔節點是紅色
143                     uncleRedFix(newNode);
144                 } else { // 叔叔節點爲 NULL 或者 是黑色節點
145                     if (newNode.key.compareTo(parent.key) > 0) { // case 1: 叔叔節點是黑色,插入到右子樹中
146                         rightNodeFix(grandfather, parent);
147                     } else { // case 2: 叔叔節點是黑色,插入到左子樹中
148                         rightRotation(parent);
149                         rightNodeFix(grandfather, newNode); // 咱們將 parent 節點做爲「新插入的節點」,這樣 真正新插入的節點 newNode 就是父節點
150                     }
151                 }
152             }
153         }
154     }
155 
156     /**
157      * 處理 父節點原來是 左節點 的 case 1 的狀況: 叔叔節點是黑色,插入到左子樹中
158      * 
159      * @param grandfather
160      * @param parent
161      */
162     private void leftNodeFix(Node grandfather, Node parent) {
163         parent.color = BLACK;
164         grandfather.color = RED;
165         rightRotation(grandfather);
166     }
167 
168     /**
169      * 處理 父節點原來是 右節點 的 case 1 的狀況: 叔叔節點是黑色,插入到右子樹中
170      * 
171      * @param grandfather
172      * @param parent
173      */
174     private void rightNodeFix(Node grandfather, Node parent) {
175         parent.color = BLACK;
176         grandfather.color = RED;
177         leftRotation(grandfather);
178     }
179 
180     /**
181      * 處理 case 3: 叔叔節點是紅色
182      * 
183      * @param newNode
184      */
185     private void uncleRedFix(Node newNode) {
186         Node parent = newNode.parentNode;
187         if ((null != parent) && (RED == parent.color)) {
188             Node grandfather = parent.parentNode;
189             Node uncle = grandfather.leftNode;
190             if (parent == grandfather.leftNode) {
191                 uncle = grandfather.rightNode;
192             }
193             parent.color = BLACK;
194             uncle.color = BLACK;
195             if (root != grandfather) {
196                 grandfather.color = RED;
197                 upsertFix(grandfather);
198             }
199         }
200     }
201 
202     /**
203      * 刪除 葉子節點 後的修復過程
204      * 
205      * @param deletedNode
206      *            被刪除的節點
207      * @param deletedNodeParent
208      *            被刪除節點的父節點
209      */
210     private void deleteLeafFix(Node deletedNode) {
211         while ((deletedNode != root) && (BLACK == deletedNode.color)) {
212             Node parent = deletedNode.parentNode;
213             Node brother = getBrother(deletedNode);
214             if (deletedNode.key.compareTo(parent.key) > 0) { // 刪除的是右葉子節點
215                 if (RED == brother.color) { // case5: 若是該兄弟節點是紅色的,那麼根據紅黑樹的特性能夠得出它的必定有兩個黑色的子節點
216                     brother.color = BLACK;
217                     brother.rightNode.color = RED;
218                     rightRotation(parent);
219                     break;
220                 } else {
221                     if ((null == brother.leftNode) && (null == brother.rightNode)) { // case4: 兄弟節點是黑色的,且沒有子節點
222                         brother.color = RED; // 將兄弟節點設爲紅色,將父節點設爲當前節點遞歸, 直到根節點,或遇到紅色節點,
223                         deletedNode = parent;
224                     } else {
225                         if ((null != brother.leftNode) && (RED == brother.leftNode.color)) {// case1:
226                                                                                             // 兄弟節點是黑色的,且有一個左節點(能夠判定
227                                                                                             // 左節點是紅色的)
228                             // case3: 兄弟節點是黑色的,且有兩個節點(能夠判定 左右節點都是紅色的) 這個和狀況 1 是同樣的
229                             brother.color = parent.color;
230                             parent.color = BLACK;
231                             brother.leftNode.color = BLACK;
232                             rightRotation(parent);
233                             break;
234                         } else {// case2: 兄弟節點是黑色的,且有一個右節點(能夠判定 右節點是紅色的)
235                             brother.rightNode.color = BLACK;
236                             brother.color = RED;
237                             leftRotation(brother);
238                         }
239                     }
240                 }
241             } else {// 刪除的是左葉子節點
242                 if (RED == brother.color) { // case5 : 若是該兄弟節點是紅色的,那麼根據紅黑樹的特性能夠得出它的必定有兩個黑色的子節點
243                     brother.color = BLACK;
244                     brother.leftNode.color = RED;
245                     leftRotation(parent);
246                     break;
247                 } else {
248                     if ((null == brother.leftNode) && (null == brother.rightNode)) { // case4: 兄弟節點是黑色的,且沒有子節點
249                         brother.color = RED; // 將兄弟節點設爲紅色,將父節點設爲當前節點遞歸, 直到根節點,或遇到紅色節點,
250                         deletedNode = parent;
251                     } else {
252                         if ((null != brother.rightNode) && (RED == brother.rightNode.color)) { // case1 :
253                                                                                                 // 兄弟節點是黑色的,且有一個右節點(能夠判定
254                                                                                                 // 右節點是紅色的)
255                             // case3 : 兄弟節點是黑色的,且有兩個節點(能夠判定 左右節點都是紅色的) 這個和狀況 1 是同樣的
256                             brother.color = parent.color;
257                             parent.color = BLACK;
258                             brother.rightNode.color = BLACK;
259                             leftRotation(parent);
260                             break;
261                         } else { // case2: 兄弟節點是黑色的,且有一個左節點(能夠判定 左節點是紅色的)
262                             brother.leftNode.color = BLACK;
263                             brother.color = RED;
264                             rightRotation(brother);
265                         }
266                     }
267                 }
268             }
269         }
270 
271         deletedNode.color = BLACK;
272     }
273 
274     private Node getBrother(Node node) {
275         if (null == node) {
276             return null;
277         }
278         Node parent = node.parentNode;
279         if (null == parent) {
280             return null;
281         }
282         if (node.key.compareTo(parent.key) > 0) {
283             return parent.leftNode;
284         } else {
285             return parent.rightNode;
286         }
287     }
288 
289     public boolean delete(K key) {
290         if (null != key) {
291             if (null != root) {
292                 return deleteNode(key, root, null);
293             }
294         }
295         return false;
296     }
297 
298     private boolean deleteNode(K key, Node current, Node parent) {
299         if (null != current) {
300             if (key.compareTo(current.key) > 0) {
301                 return deleteNode(key, current.rightNode, current);
302             }
303             if (key.compareTo(current.key) < 0) {
304                 return deleteNode(key, current.leftNode, current);
305             }
306             if (key.compareTo(current.key) == 0) {
307                 if ((null != current.leftNode) && (null != current.rightNode)) { // 將要刪除的節點下有兩個子節點
308                     dleTwoChildrenNode(current);
309                     return true;
310                 } else {
311                     if ((null == current.leftNode) && (null == current.rightNode)) { // 將要刪除的節點沒有子節點
312                         deleteLeafFix(current);
313                         if (current.key.compareTo(parent.key) > 0) {
314                             parent.rightNode = null;
315                         } else {
316                             parent.leftNode = null;
317                         }
318                         return true;
319                     } else { // 將要刪除的節點下有一個子節點,
320                         dleOneChildNode(current);
321                         return true;
322                     }
323                 }
324             }
325         }
326         return false;
327     }
328 
329     private void dleOneChildNode(Node delNode) {
330         Node replaceNode = (null == delNode.leftNode) ? delNode.rightNode : delNode.leftNode;
331         deltetLeafNode(delNode, replaceNode);
332     }
333 
334     /**
335      * 處理被刪除節點有兩個子節點的狀況
336      * 
337      * @param target
338      *            將要被刪除的節點
339      */
340     private void dleTwoChildrenNode(Node target) {
341         Node replaceNode = successor(target);
342         if ((null == replaceNode.rightNode) && (null == replaceNode.leftNode)) {
343             deltetLeafNode(target, replaceNode);
344         } else {
345             target.key = replaceNode.key;
346             target.value = replaceNode.value;
347             dleOneChildNode(replaceNode);
348         }
349     }
350 
351     private void deltetLeafNode(Node target, Node replaceNode) {
352         target.key = replaceNode.key;
353         target.value = replaceNode.value;
354         deleteLeafFix(replaceNode);
355         if (replaceNode == replaceNode.parentNode.rightNode) {
356             replaceNode.parentNode.rightNode = null;
357         } else {
358             replaceNode.parentNode.leftNode = null;
359         }
360     }
361 
362     // 找後繼結點。即,查找"紅黑樹中數據值大於該結點"的"最小結點"
363     private Node successor(Node node) {
364         if (node == null) {
365             return null;
366         }
367         if (null != node.rightNode) { // 獲取 後繼節點
368             Node p = node.rightNode;
369             while (null != p.leftNode) {
370                 p = p.leftNode;
371             }
372             return p;
373         } else {
374             Node p = node.parentNode;
375             Node ch = node;
376             while (p != null && ch == p.rightNode) {
377                 ch = p;
378                 p = p.parentNode;
379             }
380             return p;
381         }
382     }
383 
384     public static void main(String[] args) {
385 
386         RedBlackTree<Integer, String> bst = new RedBlackTree<Integer, String>();
387 
388         bst.put(100, "v100");
389         bst.put(50, "v50");
390         bst.put(150, "v150");
391         bst.put(20, "v20");
392         bst.put(85, "v85");
393         bst.put(10, "v10");
394         bst.put(15, "a15");
395         bst.put(75, "v75");
396         bst.put(95, "v95");
397         bst.put(65, "v65");
398         bst.put(76, "v76");
399         bst.put(60, "v60");
400         bst.put(66, "v66");
401         bst.put(61, "v61");
402 
403         // 當前節點是左節點 的 5中狀況
404         // bst.delete(15); // 1. 兄弟節點是黑色的,且有一個右節點(能夠判定 右節點是紅色的)
405 
406         // 2. 兄弟節點是黑色的,且有一個左節點(能夠判定 左節點是紅色的
407         // bst.put(140, "v140");
408         // bst.delete(95);
409 
410         // 4. 兄弟節點是黑色的,且沒有子節點
411         // bst.delete(66);
412 
413         // 5. 若是該兄弟節點是紅色的,那麼根據紅黑樹的特性能夠得出它的必定有兩個黑色的子節點
414         // bst.delete(95);
415         // bst.delete(15);
416 
417         System.out.println(bst.getRoot());
418     }
419 }
RedBlackTree
相關文章
相關標籤/搜索