1. 紅黑樹 -- 特性html
(1) 每一個節點或者是黑色,或者是紅色。
(2) 根節點是黑色。
(3) 每一個葉子節點是黑色。 [注意:這裏葉子節點,是指爲空的葉子節點!]
(4) 若是一個節點是紅色的,則它的子節點必須是黑色的。
(5) 從一個節點到該節點的子孫節點的全部路徑上包含相同數目的黑節點。node
2. Java -- 實現算法
package lime.xiaoniu; /** * @Author : LimeOracle * @Descri : * @Remark : * 紅黑樹的特性: (1)每一個節點或者是黑色,或者是紅色。 (2)根節點是黑色。 (3)每一個葉子節點(NIL)是黑色。 [注意:這裏葉子節點,是指爲空(NIL或NULL)的葉子節點!] (4)若是一個節點是紅色的,則它的子節點必須是黑色的。 (5)從一個節點到該節點的子孫節點的全部路徑上包含相同數目的黑節點。 */ /** * @Author : LimeOracle * @Descri : * @Remark : * 注意: (01) 特性(3)中的葉子節點,是隻爲空(NIL或null)的節點。 (02) 特性(5),確保沒有一條路徑會比其餘路徑長出倆倍。於是,紅黑樹是相對是接近平衡的二叉樹。 */ /** * @Author : LimeOracle * @Descri : * @Remark : * 1.將紅黑樹看成一顆二叉查找樹,將節點插入; * 2.將節點着色爲紅色; * 3.經過旋轉和從新着色等方法來修正該樹,使之從新成爲一顆紅黑樹。 第二步中,將插入節點着色爲"紅色"以後,不會違背"特性(5)"。那它到底會違背哪些特性呢? 對於"特性(1)",顯然不會違背了。由於咱們已經將它塗成紅色了。 對於"特性(2)",顯然也不會違背。在第一步中,咱們是將紅黑樹看成二叉查找樹,而後執行的插入操做。而根據二叉查找數的特色,插入操做不會改變根節點。因此,根節點仍然是黑色。 對於"特性(3)",顯然不會違背了。這裏的葉子節點是指的空葉子節點,插入非空節點並不會對它們形成影響。 對於"特性(4)",是有可能違背的! 那接下來,想辦法使之"知足特性(4)",就能夠將樹從新構形成紅黑樹了。 */ /** * @Author : LimeOracle * @Descri : * @Remark : * 根據被插入節點的父節點的狀況,能夠將"當節點z被着色爲紅色節點,並插入二叉樹"劃分爲三種狀況來處理。 ① 狀況說明:被插入的節點是根節點。 處理方法:直接把此節點塗爲黑色。 ② 狀況說明:被插入的節點的父節點是黑色。 處理方法:什麼也不須要作。節點被插入後,仍然是紅黑樹。 ③ 狀況說明:被插入的節點的父節點是紅色。 處理方法:那麼,該狀況與紅黑樹的「特性(4)」相沖突。 這種狀況下,被插入節點是必定存在非空祖父節點的; 進一步的講,被插入節點也必定存在叔叔節點(即便叔叔節點爲空,咱們也視之爲存在,空節點自己就是黑色節點)。 理解這點以後,咱們依據"叔叔節點的狀況",將這種狀況進一步劃分爲3種狀況(Case)。 */ import com.sun.org.apache.regexp.internal.RE; import static lime.xiaoniu.RBTree.Color.BLACK; import static lime.xiaoniu.RBTree.Color.RED; /** * @Author : LimeOracle * @Descri : 依據"叔叔節點的狀況",將這種狀況進一步劃分爲3種狀況(Case)。 * @Remark : * Case 1 * B B * / \ / \ * R R 或 R R * / \ * CR CR * 當前節點的父節點是紅色,且當前節點的祖父節點的另外一個子節點(叔叔節點)也是紅色。 * (01) 將「父節點」設爲黑色。 (02) 將「叔叔節點」設爲黑色。 (03) 將「祖父節點」設爲「紅色」。 (04) 將「祖父節點」設爲「當前節點」(紅色節點);即,以後繼續對「當前節點」進行操做。 * Case 2 * B B * / \ / \ * R B 或 B R * \ / * CR CR * 當前節點的父節點是紅色,叔叔節點是黑色,且當前節點是其父節點的右孩子 * (01) 將「父節點」做爲「新的當前節點」。 (02) 以「新的當前節點」爲支點進行左旋。 * Case 3 * B B * / \ / \ * R B 或 B R * / \ * CR CR * 當前節點的父節點是紅色,叔叔節點是黑色,且當前節點是其父節點的左孩子 * (01) 將「父節點」設爲「黑色」。 (02) 將「祖父節點」設爲「紅色」。 (03) 以「祖父節點」爲支點進行右旋。 * * 上面三種狀況(Case)處理問題的核心思路都是:將紅色的節點移到根節點;而後,將根節點設爲黑色。 */ public class RBTree<T extends Comparable<T>> { private RBTNode<T> mRoot; enum Color{RED,BLACK} public class RBTNode<T extends Comparable<T>>{ Color color;//顏色 T key; //關鍵字(鍵值) RBTNode<T> left;//左孩子 RBTNode<T> right;//右孩子 RBTNode<T> parent;//父節點 public RBTNode(T key,Color color,RBTNode<T> parent,RBTNode<T> left,RBTNode<T> right){ this.key = key; this.color = color; this.parent = parent; this.left = left; this.right = right; } @Override public String toString() { return "RBTNode{" + "color=" + color + ", key=" + key + ", left=" + left + ", right=" + right + ", parent=" + parent + '}'; } } private void leftRotate(RBTNode<T> x){ RBTNode<T> y = x.right; x.right = y.left; if(null != y.left){ y.left.parent = x; } y.parent = x.parent; if(null == x.parent){ this.mRoot = y; }else{ if(x.parent.left == x){ x.parent.left = y; }else { x.parent.right = y; } } y.left = x; x.parent = y; } private void rightRotate(RBTNode x){ RBTNode y = x.left; x.left = y.right; if(null != y.right){ y.right.parent = x; } y.parent = x.parent; if(null == x.parent){ this.mRoot = y; }else{ if(x.parent.left == x){ x.parent.left = y; }else{ x.parent.right = y; } } y.right = x; x.parent = y; } private void insert(RBTNode<T> node){ int cmp; RBTNode<T> y = null; RBTNode<T> x = this.mRoot; // 1. 將紅黑樹看成一顆二叉查找樹,將節點添加到二叉查找樹中。 while (x != null){ y = x; cmp = node.key.compareTo(x.key); if(cmp < 0){ x = x.left; }else{ x = x.right; } } node.parent = y; if(null != y){ cmp = node.key.compareTo(y.key); if(cmp < 0){ y.left = node; }else{ y.right = node; } }else{ this.mRoot = node; } // 2. 設置節點的顏色爲紅色 node.color = RED; // 3. 將它從新修正爲一顆二叉查找樹 insertFixUp(node); } public void insert(T key){ RBTNode<T> node = new RBTNode<T>(key,BLACK,null,null,null); if(null != node){ insert(node); } } /** * @Author : LimeOracle * @Descri : 紅黑樹插入修正函數 * @Remark : * 在向紅黑樹中插入節點以後(失去平衡),再調用該函數; * 目的是將它從新塑形成一顆紅黑樹。 */ private void insertFixUp(RBTNode<T> node){ RBTNode<T> parent,gparent; // 若「父節點存在,而且父節點的顏色是紅色」 while ((parent = node.parent) != null && RED == parent.color){ gparent = parent.parent; //若「父節點」是「祖父節點的左孩子」 if(parent == gparent.left){ // Case 1條件:叔叔節點是紅色 // (01) 將「父節點」設爲黑色。 // (02) 將「叔叔節點」設爲黑色。 // (03) 將「祖父節點」設爲「紅色」。 // (04) 將「祖父節點」設爲「當前節點」(紅色節點);即,以後繼續對「當前節點」進行操做。 RBTNode<T> uncle = gparent.right; if(uncle != null && RED == uncle.color){ parent.color = BLACK; uncle.color = BLACK; gparent.color = RED; node = gparent; continue; } // Case 2條件:叔叔是黑色,且當前節點是右孩子 // (01) 將「父節點」做爲「新的當前節點」。 // (02) 以「新的當前節點」爲支點進行左旋。 if(parent.right == node){ RBTNode<T> tmp; leftRotate(parent); tmp = parent;parent = node;node = tmp; } // Case 3條件:叔叔是黑色,且當前節點是左孩子。 if(parent.left == node){ // (01) 將「父節點」設爲「黑色」。 // (02) 將「祖父節點」設爲「紅色」。 // (03) 以「祖父節點」爲支點進行右旋。 parent.color = BLACK; gparent.color = RED; rightRotate(gparent); } }else{ //若「z的父節點」是「z的祖父節點的右孩子」 // Case 1條件:叔叔節點是紅色 RBTNode<T> uncle = gparent.left; if(uncle.color == RED){ parent.color = BLACK; uncle.color = BLACK; gparent.color = RED; node = gparent; continue; } // Case 2條件:叔叔是黑色,且當前節點是左孩子 if(parent.left == node){ RBTNode<T> tmp; rightRotate(parent); tmp = parent;parent = node;node = parent; } // Case 3條件:叔叔是黑色,且當前節點是右孩子。 if(parent.right == node){ parent.color = BLACK; gparent.color = RED; leftRotate(gparent); } } } this.mRoot.color = BLACK; } public void inOrder(){ inOrder(this.mRoot); } private void inOrder(RBTNode<T> x){ if(null == x){ return; } inOrder(x.left); System.out.print(x.key + " "); inOrder(x.right); } public static void main(String[] args){ RBTree<Integer> root = new RBTree<Integer>(); root.insert(80); root.insert(40); root.insert(120); root.insert(20); root.insert(60); root.insert(100); root.insert(140); root.insert(10); root.insert(50); root.insert(90); root.insert(10); root.insert(30); root.inOrder(); } }
3. 鳴謝:apache
啦啦啦post