這幾種樹都屬於數據結構中較爲複雜的,在平時面試中,常常會問理解用法,但通常不會問具體的實現,因此今天來梳理一下這幾種樹之間的區別與聯繫,感謝知乎用戶@Cailiang,這篇文章參考了他的專欄。node
是一棵空樹,或是具備下列性質的二叉樹:面試
若它的左子樹不空,則左子樹上全部結點的值均小於它的根結點的值; 若它的右子樹不空,則右子樹上全部結點的值均大於它的根結點的值;它的左、右子樹也分別爲二叉排序樹。數據庫
1 若是根節點爲空,則將插入的節點做爲根節點數據結構
2 不然和根節點比較(咱們是經過key來比較,因此 K 必須是實現了Comparable接口的對象)若是比根節點小,則新節點會插入到左子樹中,若是比根節點大,則新節點會插入到右子樹中,若是和根節點相等,則更新根節點的值ide
1 若是根節點爲空,返回 null性能
2 若是,key 和根節點的key相同,則返回根節點this
3 若是,key比根節點的key小,則遞歸查找根節點的左節點spa
4 若是,key比根節點的key大,則遞歸查找根節點的右節點指針
1 將要刪除的節點沒有子節點 ----> 直接刪除code
2 將要刪除的節點下有一個子節點 -----> 將要被刪除的節點的子節點掛靠到將要被刪除的節點的父節點上便可
3 將要刪除的節點下有兩個子節點 ----> 在將要被刪除的節點的右子樹中找到一個最小節點,而後用找到的最小節點與須要刪除的節點替換。最後再將最小節點進行刪除。(這裏你可能有其它的方案,咱們這裏這麼作的緣由是一,右子樹的最小節點必定沒有左節點,處理起來會簡單一些,二,這樣能夠保持樹的高度不變,甚至是下降樹的高度,樹的高度越低意味着操做的時間複雜度越低。因此實現刪除一個節點的原則是,用最小的代價實現刪除的功能,而且保持樹的高度不變甚至下降樹的高度)
搜索二叉樹的增刪改查的性能都不錯,可是在一些特殊狀況下,搜索二叉樹的性能可能由對數型變成線性,性能大大下降,好比插入的一組數據是有序的,那麼二叉搜索樹的結構將變成一個鏈表時間複雜度變爲 O(N),也就是說插入一組有序或局部有序的數據將會致使二叉搜索樹不平衡,樹的高度會很大,時間複雜度會趨近於 O(N)。
平衡二叉樹,首先是一棵二叉查找樹,可是它知足一點重要的特性:每個結點的左子樹和右子樹的高度差最多爲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 節點到根節點的路徑上的黑色節點數量必定是相同的
前面介紹了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中一個含有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的每一個節點都包含了關鍵字(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 }
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 }