紅黑樹的性質:java
black-height--紅黑樹從根節點到每一個葉子節點的路徑都包含相同數量的黑色節點,所以從根節點到葉子節點的路徑中包含的黑色節點數被稱爲樹的「黑色高度(black-height.node
因爲紅黑樹只是一個特殊的排序二叉樹,紅黑樹的插入刪除和排序二叉樹相同,只是影響了平衡性能,所以須要插入和刪除後修復.性能
插入及其修復代碼:測試
public void insert(Node node) { node.color = RED; Node cur = mroot; Node tmp = null; int num; //找到要插入的父節點 while (cur != null) { tmp = cur; num = cmp.compare(tmp, node); cur = num > 0 ? tmp.left : tmp.right; } node.parent = tmp; if (tmp != null) { num = cmp.compare(tmp, node); if (num >= 0) { tmp.left = node; } else { tmp.right = node; } } else { mroot = node; } fixInsertRBT(node); }
private void fixInsertRBT(Node node) { Node p, g, u; //狀況3,4 while (node.parent != null && node.parent.color == RED) { p = node.parent; g = p.parent; //狀況3:p爲g的左孩子 if (p == g.left) { //g一定不會爲空當沒有祖父節點的時候就不會跳進此循環 u = g.getRight(); //若是p,u都爲紅色. if (u != null && u.color == RED) { p.color = BLACK; u.color = BLACK; g.color = RED; node = g; } else { //當node爲右孩子的時候,先左旋,使得不至於直接旋轉致使右邊過長,從而形成不平衡. if (node == p.right) { rotateLeft(p); Node tmp = p; p = node; node = tmp; } //當node 爲左孩子的時候 p.color = BLACK; g.color = RED; rotateRight(g); } } //狀況4--p爲g的右孩子 else { u = g.getLeft(); if (u != null && u.color == RED) { p.setColor(BLACK); u.setColor(BLACK); g.setColor(RED); node = g; } else { if (node == p.left) { rotateRight(p); Node tmp = p; p = node; node = tmp; } //當node 爲左孩子的時候 p.color = BLACK; g.color = RED; rotateLeft(g); } } } //知足狀況1,實際上增長黑色節點高度一定時從這裏修改的,由於下面爲了保持性質5黑色節點高度 // 不會增長,可是當新增節點最後致使根節點變爲了紅色,此時就會增長紅黑樹黑色節點高度. mroot.setColor(BLACK); }
左旋及右旋的代碼:.net
void rotateLeft(Node data) { Node temp = data.right; data.right = temp.left; if (data.right != null) { data.right.parent = data; } temp.parent = data.parent; if (data.parent == null) { mroot = temp; } else if (data.parent.left == data) { temp.parent.left = temp; } else { temp.parent.right = temp; } temp.left = data; data.parent = temp; } void rotateRight(Node data) { Node temp = data.left; data.left = temp.right; if (data.left != null) { data.left.parent = data; } //交換父節點 if (data.parent == null) { mroot = temp; } else if (data.parent.left == data) { data.parent.left = temp; } else { data.parent.right = temp; } temp.parent = data.parent; temp.right = data; data.parent = temp; }
具體演化過程見我手畫的圖 code
p.parent=p.parent.parent
,依次找到其父節點們第一個爲左子樹的狀況,則此祖父節點爲p的後繼節點.Node successor(Node data) { if (data == null) { return null; } //後繼節點是右節點爲根的整棵樹上的最小節點 else if (data.right != null) { return findMinNode(data.right); } //若是沒有右節點,則必爲其第一顆爲左子樹的祖父(包括父親)節點(由於其祖父類節點若爲右子樹, // 一定小於其前一個祖父類節點,一次類推,須要第一顆左子樹祖父類節點,若一直到頭沒有,則說明 // 刪除的是整個樹的最大節點,故後繼節點爲null) else { Node s = data.parent; Node tmp = data; while (s != null && tmp == s.right) { tmp = s; s = s.parent; } return s; } } Node findMinNode(Node root) { if (root == null) { return null; } else { Node min = root; while (min.left != null) { min = min.left; } return min; } }
String remove(int key) { Node node = getNode(key); if (node == null) { return null; } String oldValue = node.value; remove(node); // deleteNode(node); return oldValue; }
private void remove(Node node) { Node p = node; //將被刪除的節點改成刪除其後繼節點,並把後繼節點的值copy過來 if (node.left != null && node.right != null) { Node s = successor(node); p.key = s.key; p.value = s.value; p = s; } //replacement有4中狀況得來,(1)只有左孩子(2)只有右孩子 //(3)左孩子右孩子都不爲空時其後繼節點的左孩子必爲空. //(4)爲空,又分爲兩種狀況:一是被刪除的節點是惟一一個節點,二是被刪除的節點是葉子節點. Node replacement = (p.left == null ? p.right : p.left); if (replacement != null) { replacement.parent = p.parent; //刪除的是跟節點 if (p.parent == null) { mroot = replacement; } else if (p.parent.left == p) { p.parent.left = replacement; } else { p.parent.right = replacement; } p.left = p.right = p.parent = null; if (p.color == BLACK) { //修復可能產生兩個紅色節點 fixDeletionRBT(p); } } //單獨的一個節點 else if (p.parent == null) { mroot = null; } //葉子節點 else { if (p.parent.left == p) { p.parent.left = null; } else { p.parent.right = null; } p.parent = null; if (p.color == BLACK) { //修復葉子節點可能爲紅色 fixDeletionRBT(p); } } }
測試:懶得沒有寫全,基本上人工看了一下.blog
@Test public void remove() throws Exception { Node node8 = new Node(8, "8"); Node node4 = new Node(4, "4"); Node node12 = new Node(12, "12"); Node node2 = new Node(2, "2"); Node node6 = new Node(6, "6"); Node node10 = new Node(10, "10"); Node node14 = new Node(14, "14"); Node node1 = new Node(1, "1"); Node node3 = new Node(3, "3"); Node node5 = new Node(5, "5"); Node node7 = new Node(7, "7"); Node node9 = new Node(9, "9"); Node node11 = new Node(11, "11"); Node node13 = new Node(13, "13"); Node node15 = new Node(15, "15"); keyStore.insert(node8); keyStore.insert(node4); keyStore.insert(node12); keyStore.insert(node2); keyStore.insert(node6); keyStore.insert(node10); keyStore.insert(node14); keyStore.insert(node1); keyStore.insert(node3); keyStore.insert(node5); keyStore.insert(node7); keyStore.insert(node9); keyStore.insert(node11); keyStore.insert(node13); keyStore.insert(node15); //測試刪除根節點-----至關於既有left,又有right keyStore.levelScan(); keyStore.remove(8); System.out.println("刪除節點之後"); keyStore.levelScan(); //測試刪除節點只有右子樹 keyStore.remove(10); System.out.println("刪除節點之後"); keyStore.levelScan(); //測試刪除葉子節點 keyStore.remove(15); System.out.println("刪除節點之後"); keyStore.levelScan(); //測試刪除節點只有左子樹 keyStore.remove(14); System.out.println("刪除節點之後"); keyStore.levelScan(); //測試刪除葉子節點 keyStore.remove(3); keyStore.levelScan(); keyStore.removeAll(); keyStore.insert(new Node(1,"1")); //刪除單節點 keyStore.remove(1); keyStore.levelScan(); }
RBT排序