红黑树 -- 增删查改
唉 规整规整 进阶目录
1. 红黑树 -- 特性
(1) 每个节点或者是黑色,或者是红色。
(2) 根节点是黑色。
(3) 每个叶子节点是黑色。 [注意:这里叶子节点,是指为空的叶子节点!]
(4) 如果一个节点是红色的,则它的子节点必须是黑色的。
(5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
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. 鸣谢:
啦啦啦