声明:这是转载的
在普通二叉树中查找,插入,删除数据项都很快,但是当插入的数据都是有序数据时,二叉树就是非平衡的了,极端情况是为链表;
查找速度下降到0(N) 而不是平衡树的O(logN)。
【红黑规则】
①每一个节点不是红色的就是黑色的。
②根总是黑色的。
③如果节点是红色的,则他的子节点必须是黑色的(反之不一定必须为真)。
④从根到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点。
修正违规的情况
①改变节点的颜色
②执行旋转操作
左旋
伪代码--算法导论:
LEFT-ROTATE(T, x)
y ← right[x] // 前提:这里假设x的右孩子为y。下面开始正式操作
right[x] ← left[y] // 将 “y的左孩子” 设为 “x的右孩子”,即 将β设为x的右孩子
p[left[y]] ← x // 将 “x” 设为 “y的左孩子的父亲”,即 将β的父亲设为x
p[y] ← p[x] // 将 “x的父亲” 设为 “y的父亲”
if p[x] = nil[T]
then root[T] ← y // 情况1:如果 “x的父亲” 是空节点,则将y设为根节点
else if x = left[p[x]]
then left[p[x]] ← y // 情况2:如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
else right[p[x]] ← y // 情况3:(x是它父节点的右孩子) 将y设为“x的父节点的右孩子”
left[y] ← x // 将 “x” 设为 “y的左孩子”
p[x] ← y // 将 “x的父节点” 设为 “y”
右旋
伪代码--算法导论:
RIGHT-ROTATE(T, y)
x ← left[y] // 前提:这里假设y的左孩子为x。下面开始正式操作
left[y] ← right[x] // 将 “x的右孩子” 设为 “y的左孩子”,即 将β设为y的左孩子
p[right[x]] ← y // 将 “y” 设为 “x的右孩子的父亲”,即 将β的父亲设为y
p[x] ← p[y] // 将 “y的父亲” 设为 “x的父亲”
if p[y] = nil[T]
then root[T] ← x // 情况1:如果 “y的父亲” 是空节点,则将x设为根节点
else if y = right[p[y]]
then right[p[y]] ← x // 情况2:如果 y是它父节点的右孩子,则将x设为“y的父节点的左孩子”
else left[p[y]] ← x // 情况3:(y是它父节点的左孩子) 将x设为“y的父节点的左孩子”
right[x] ← y // 将 “y” 设为 “x的右孩子”
p[y] ← x // 将 “y的父节点” 设为 “x”
插入一个新节点
- 第一步将红黑树当做一棵二叉查找树,将节点插入。
- 第二步将插入的节点着色为 红色。
- 通过一系列旋转或着色操作,使之重新成为一棵红黑树。
-
-
-
-
- 重温一下规则
① 每个节点或者是黑色,或者是红色。
② 根节点是黑色。
③ 如果一个节点是红色的,则它的子节点必须是黑色的,不能存在两个连续的红色节点。
④ 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
新插入的节点不会违反规则④,只有可能违反规则③。违反规则出现的三种情况插入操作-case 1 case 1的操作是将父节点和叔叔节点与祖父节点的颜色互换,这样就符合了RBTRee的定义。即维持了高度的平衡,修复后颜色也符合RBTree定义的第三条和第四条。下图中,操作完成后A节点变成了新的节点。如果A节点的父节点不是黑色的话,则继续做修复操作。
插入操作-case 2叔叔节点为黑,且祖父节点,父节点和新节点不在一条斜线上
1.以父亲为支点-左旋
2.把原来的父亲节点标记为旋转后的当前节点
3.把原来的当前节点标记为旋转后的父亲节点
case2 的情况 转变为 case 3
插入操作-case 3
叔叔节点为黑,且祖父节点,父节点和新节点在一条斜线上
1.将父节点设为黑
2.将祖父节点设为红
3.以祖父节点为支点-右旋
删除一个节点
- 将红黑树当做一棵二叉查找树,将节点删除。
- 修正该树,使之重新重新成为一棵红黑树
① 被删除节点没有儿子,即为叶节点。那么,直接将该节点删除就OK了。
② 被删除节点只有一个儿子。那么,直接删除该节点,并用该节点的唯一子节点顶替它的位置。
③ 被删除节点有两个儿子。那么,先找出它的后继节点;然后把“它的后继节点的内容”复制给“该节点的内容”;之后,删除“它的后继节点”。在这里,后继节点相当于替身,在将后继节点的内容复制给"被删除节点"之后,再将后继节点删除。这样就巧妙的将问题转换为"删除后继节点"的情况了,下面就考虑后继节点。 在"被删除节点"有两个非空子节点的情况下,它的后继节点不可能是双子非空。既然"的后继节点"不可能双子都非空,就意味着"该节点的后继节点"要么没有儿子,要么只有一个儿子。若没有儿子,则按"情况① "进行处理;若只有一个儿子,则按"情况② "进行处理。删除修正操作
总体思想:
从兄弟节点借调黑色节点使树保持局部的平衡
如果兄弟节点没有黑色节点可以借调的话,就只能往上追溯,
将每一级的黑色节点数减去一个,使整棵树符合定义
删除修复四种情况:
- 待删除的节点的兄弟节点是红色的节点。
- 待删除的节点的兄弟节点是黑色的节点,且兄弟节点的子节点都是黑色的。
- 待调整的节点的兄弟节点是黑色的节点,且兄弟节点的左子节点是红色的,右节点是黑色的(兄弟节点在右边),如果兄弟节点在左边的话,就是兄弟节点的右子节点是红色的,左节点是黑色的。
- 待调整的节点的兄弟节点是黑色的节点,且右子节点是是红色的(兄弟节点在右边),如果兄弟节点在左边,则就是对应的就是左节点是红色的。
Case 1:兄弟节点是红色的由于兄弟节点是红色节点的时候,无法借调黑节点,所以需要将兄弟节点提升到父节点,由于兄弟节点是红色的,根据RBTree的定义,兄弟节点的子节点是黑色的,就可以从它的子节点借调了。
之所以要做case 1操作是因为兄弟节点是红色的,无法借到一个黑节点来填补删除的黑节点。
Case 2:兄弟是黑色的,且兄弟两个孩子也都是黑色的case 2的删除操作是由于兄弟节点可以消除一个黑色节点,因为兄弟节点和兄弟节点的子节点都是黑色的,所以可以将兄弟节点变红,这样就可以保证树的局部的颜色符合定义了。这个时候需要将父节点A变成新的节点,继续向上调整,直到整颗树的颜色符合RBTree的定义为止。
Case 3:兄弟是黑色的,兄弟的左孩子是红色,右孩子黑色兄弟是黑色的,兄弟的左孩子是红色,右孩子黑色case 3的删除操作是一个中间步骤,它的目的是将左边的红色节点借调过来,这样就可以转换成case 4状态了,在case 4状态下可以将D,E节点都阶段过来,通过将两个节点变成黑色来保证红黑树的整体平衡。
之所以说case-3是一个中间状态,是因为根据红黑树的定义来说,下图并不是平衡的,他是通过case 2操作完后向上回溯出现的状态。之所以会出现case 3和后面的case 4的情况,是因为可以通过借用侄子节点的红色,变成黑色来符合红黑树定义4.
Case 4:兄弟节点是黑色的,并且兄弟右孩子是红色,兄弟节点左孩子任意颜色case 4的操作是真正的节点借调操作,通过将兄弟节点以及兄弟节点的右节点借调过来,并将兄弟节点的右子节点变成红色来达到借调两个黑节点的目的,这样的话,整棵树还是符合RBTree的定义的。
Case 4这种情况的发生只有在待删除的节点的兄弟节点为黑,且子节点不全部为黑,才有可能借调到两个节点来做黑节点使用,从而保持整棵树都符合红黑树的定义。
红黑树效率
和一般的二叉搜索树类似,红黑树的查找、插入和删除的时间复杂度为O(log2N),额外的开销只是每一个节点的存储空间都稍微稍微增加了一点;红黑树的查找效率要高于普通二叉树,因为他属于平衡二叉树。在大多数应用中,查找的次数比插入和删除的次数多,所以应用红黑树不会增加太多的时间开销。
AVL树AVL是最早的一种平衡树,发明者Adelson-Velskii 和Landis,在AVL树每个节点存储一个额外的数据,他左子树和右子树的高度差,这个差值不会大于1,这也就是说,节点的左子树的高度和右子树的高度相差不会大于一层。AVL树查找的时间复杂度为O(logN),因为树一定是平衡的。但是由于插入或删除一个节点时需要扫描两趟树,一次向下查找插入点,一次向上平衡树,AVL树不如红黑树效率高,也不如红黑树常用。
2-3-4树简单的说2-3-4是一种多叉树,他每个节点最多有四个子节点和三个数据项。主要的应用是在外部存储来组织数据(通常指磁盘驱动器)。
2-3-4 树在计算机科学中是阶为 4 的B树。根据维基百科上的介绍:大体上同B树一样,2-3-4 树是可以用做字典的一种自平衡数据结构。它可以在O(log n)时间内查找、插入和删除,这里的 n 是树中元素的数目。2-3-4 树在多数编程语言中实现起来相对困难,因为在树上的操作涉及大量的特殊情况。红黑树实现起来更简单一些,所以可以用它来替代。
2,、3、4的含义是指一个节点可能含有的子节点的个数。有一个数据项的节点总是有两个子节点。有两个数据项的节点总是有三个子节点。有三个数据项的节点总是有四个子节点。
【附录】
红黑树属于平衡二叉树。
说它不严格是因为它不是严格控制左、右子树高度或节点数之差小于等于1。
但红黑树高度依然是平均log(n),且最坏情况高度不会超过2log(n),这有数学证明。所以它算平衡树,只是不严格。不过严格与否并不影响数据结构的复杂度。
红黑树多用于系统底层,java 的几个集合类型。底层实现,就用到了树算法。
网络来源部分
http://bbs.csdn.net/topics/280022320
java中的TreeMap为什么要用红黑树实现,而不用AVL树实现呢?我感觉AVL树更高效啊
[Content]
大家都应该知道树的效率是和高度height高度紧密的联系着
所谓的效率是指insert,delete,search
AVL tree的height < 1.75 log2(n)
红黑树的 height <= 2 log2(n+1)
n是树中所有元素的个数。
很明显AVL树的高度height比红黑树小,那为什么java中的TreeMap不用AVL树来实现呢?
[A yami251139]
红黑树修改,插入比avl快点
avl查询比较快
一般用到算法。。。大多不是查询吧。。。
http://blog.csdn.net/yaoweijq/article/details/5833770
红黑树java版
http://blog.csdn.net/yangchuxi/article/details/6745488
红黑树详讲(附上部分java代码)
http://www.ibm.com/developerworks/cn/java/j-lo-tree/
通过分析 JDK 源代码研究 TreeMap 红黑树算法实现
【2014-04-06】
【参照】
1.算法导论2.https://www.cnblogs.com/skywang12345/p/3245399.html#aa5红黑树(一)之 原理和算法详细介绍 skywang123453.https://tech.meituan.com/redblack-tree.html
红黑树深入剖析及Java实现 美团4.https://www.cnblogs.com/skywang12345/p/3624343.html#a3
红黑树(五)之 Java的实现 skywang12345
【代码】package cn.demo.tree; /** * @author yangw * @version V1.0 * @Date 2017年12月28日 下午4:17:35 * @Email * @Description: 红黑二叉树 * 红黑树基本规则 * ①任何一个节点都有颜色,黑色或者红色 * ②根节点是黑色的 * ③父子节点之间不能出现两个连续的红节点 * ④任何一个节点向下遍历到其子孙的叶子节点,所经过的黑节点个数必须相等 */ public class RBTree<T extends Comparable<T>> { private RBTreeNode<T> root; private static final boolean RED = false; private static final boolean BLACK = true; public RBTree() {} class RBTreeNode<T extends Comparable<T>> { private boolean color; //颜色 private T key; //node value private RBTreeNode<T> left; //left child private RBTreeNode<T> right; //right child private RBTreeNode<T> parent; //父节点 public RBTreeNode(T key, boolean color, RBTreeNode<T> parent, RBTreeNode<T> left, RBTreeNode<T> right) { super(); this.key = key; this.color = color; this.parent = parent; this.left = left; this.right = right; } public T getKey(){ return key; } public String toString() { return "" + key + (this.color == RED ? "(R)" : "B"); } } /** * 获取传入参数节点的父节点 * @param node * @return */ private RBTreeNode<T> parentOf(RBTreeNode<T> node){ return node != null ? node.parent : null; } private boolean colorOf(RBTreeNode<T> node){ return node == null ? BLACK : node.color; } private boolean isRed(RBTreeNode<T> node){ return ((node != null) && (node.color == RED)) ? true : false; } private boolean isBlack(RBTreeNode<T> node){ return ((node != null) && (node.color == BLACK)) ? true : false; } private void setBlack(RBTreeNode<T> node){ if(node != null) node.color = BLACK; } private void setRed(RBTreeNode<T> node){ if(node != null) node.color = RED; } private void setParent(RBTreeNode<T> node, RBTreeNode<T> parent){ if(node != null) node.parent = parent; } private void setColor(RBTreeNode<T> node, boolean color){ if(node != null) node.color = color; } /** * 前序遍历 */ private void preOrder(RBTreeNode<T> node){ if(node != null){ System.out.println(node.key + " "); preOrder(node.left); preOrder(node.right); } } public void preOrder(){ preOrder(root); } /** * 中序遍历 * @param node */ private void inOrder(RBTreeNode<T> node){ if(node != null){ inOrder(node.left); System.out.println(node.key + " "); inOrder(node.right); } } public void inOrder(){ inOrder(root); } /** * 后序遍历 * @param node */ private void postOrder(RBTreeNode<T> node){ postOrder(node.left); postOrder(node.right); System.out.println(node.key + " "); } public void postOrder(){ postOrder(root); } /* * (递归实现)查找"红黑树x"中键值为key的节点 */ private RBTreeNode<T> search(RBTreeNode<T> x, T key) { if (x==null) return x; int cmp = key.compareTo(x.key); if (cmp < 0) return search(x.left, key); else if (cmp > 0) return search(x.right, key); else return x; } public RBTreeNode<T> search(T key) { return search(root, key); } /* * (非递归实现)查找"红黑树x"中键值为key的节点 */ private RBTreeNode<T> iterativeSearch(RBTreeNode<T> x, T key) { while (x!=null) { int cmp = key.compareTo(x.key); if (cmp < 0) x = x.left; else if (cmp > 0) x = x.right; else return x; } return x; } public RBTreeNode<T> iterativeSearch(T key) { return iterativeSearch(root, key); } /** * 查找最小节点 * @return */ private RBTreeNode<T> minNode(){ if(root == null) return null; RBTreeNode<T> current = root; while(current.left != null){ current = current.left; } return current; } public T minmum(){ RBTreeNode<T> node = minNode(); if(node == null) return null; return node.key; } private RBTreeNode<T> maxNode(){ if(root == null) return null; RBTreeNode<T> current = root; while(current.right != null){ current = current.right; } return current; } public T maxmum(){ RBTreeNode<T> node = maxNode(); if(node == null) return null; return node.key; } /** * 返回叔叔节点 * @param node * @return */ private RBTreeNode<T> getUncle(RBTreeNode<T> node){ if(node == null) return null; RBTreeNode<T> parent = node.parent; RBTreeNode<T> ancestor; if(parent != null){ ancestor = parent.parent; }else{ return null; } if(parent == ancestor.left) return ancestor.right; return ancestor.left; } /** * 对红黑树的节点(x)进行左旋转 * 左旋示意图(对节点x进行左旋): * px px * / / * x y * / \ --(左旋)-. / \ * lx y x ry * / \ / \ * ly ry lx ly */ private void leftRotate(RBTreeNode<T> x){ //1.设置x的右孩子为y RBTreeNode<T> y = x.right; //2.将y的左孩子设置为x的右孩子 x.right = y.left; //3.如果y的左孩子非空,,将x设为y左孩子的父亲 if(y.left != null){ y.left.parent = x; } //4.把x的父亲指向y if(x.parent == null){ root = y; //add by yangw 2018/01/04 y.parent = null; }else{ if(x.parent.left == x) x.parent.left = y; else x.parent.right = y; /** * 把y的父亲指向x的父节点 * add by yangw 2018/01/04 */ y.parent = x.parent; } //5.把x设为y的左孩子 y.left = x; //6.把x的父节点设为y x.parent = y; } /** * 对红黑树的节点(y)进行右旋转 * 右旋示意图(对节点y进行右旋): * py py * / / * y x * / \ --(右旋)-. / \ * x ry lx y * / \ / \ * lx rx rx ry */ private void rightRotate(RBTreeNode<T> y){ //1.设置x为当前节点的左孩子 RBTreeNode<T> x = y.left; //2.将x的右孩子设为y的左孩子 y.left = x.right; //3.如x右孩子不为空,将y设为x右孩子父亲 if(x.right != null){ x.right.parent = y; } //4.把y的父亲指向x if(y.parent == null){ root = x; //add by yangw 2018/01/04 x.parent = null; }else{ if(y.parent.left == y) y.parent.left = x; else y.parent.right = x; //add yangw 2018/01/04 x.parent = y.parent; } //5.将y设为x的右孩子 x.right = y; //6.把x设为y的父节点 y.parent = x; } /** * 把节点插入到红黑树中 * @param node */ private void insert(RBTreeNode<T> node){ int cmp; RBTreeNode<T> y = null; RBTreeNode<T> x = root; // 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(y == null){ root = node; }else{ cmp = node.key.compareTo(y.key); if(cmp < 0) y.left = node; else y.right = node; } //2.新插入节点颜色设置为红色 node.color = RED; //3.插入后重新修正红黑二叉树 insertFixUp(node); } public void insert(T key){ RBTreeNode<T> node = new RBTreeNode<T>(key, RED, null, null, null); if(node != null) insert(node); } /** * 在向红黑树中插入节点之后(失去平衡),再调用该函数 * 目的是将它重新塑造成一颗红黑树。 * @param node 新插入的节点 (对应《算法导论》中的z) * 插入的核心思路是: * 将红色的节点上移到根节点;最后,将根节点设为黑色(正确) */ private void insertFixUp(RBTreeNode<T> node){ RBTreeNode<T> parent; //父节点 RBTreeNode<T> gparent; //祖父节点 /** * 只有在父节点为红色节点的时候是需要插入修复操作的 * 三种情况 * 1.叔叔节点也为红色 * 2.叔叔节点为黑,且祖父节点,父节点和新节点不在一条斜线上 * 3.叔叔节点为黑,且祖父节点,父节点和新节点在一条斜线上 */ /** * 若父节点存在,并且父节点的颜色是红色 * 自底向上,一级一级向上校正 */ while(((parent = parentOf(node)) != null) && isRed(parent)){ gparent = parentOf(parent); //若父节点是祖父节点的左孩子 if(parent == gparent.left){ /** * case 1: 叔叔节点是红色 */ RBTreeNode<T> uncle = gparent.right; if((uncle != null) && isRed(uncle)){ setBlack(uncle); setBlack(parent); setRed(gparent); //*回溯-以祖父节点为起点 node = gparent; continue; } /** * case 2: 叔叔是黑色,且当前节点是右孩子--不在一条斜线上 * 1.以父亲为支点-左旋 * 2.把原来的父亲节点标记为旋转后的当前节点 * 3.把原来的当前节点标记为旋转后的父亲节点 * case2 的情况 转变为 case 3 */ if(parent.right == node){ RBTreeNode<T> tmp; leftRotate(parent); tmp = parent; parent = node; node = tmp; } /** * case 3: 叔叔是黑色,且当前节点是左孩子--祖父节点、父节点、子节点在一条斜线上 * 1.将父节点设为黑 * 2.将祖父节点设为红 * 3.以祖父节点为支点-右旋 */ setBlack(parent); setRed(gparent); rightRotate(gparent); }else{ //父节点是祖父节点的右孩子 /** * case 1: 叔叔节点是红色 */ RBTreeNode<T> uncle = gparent.left; if((uncle != null) && isRed(uncle)){ setBlack(uncle); setBlack(parent); setRed(gparent); //*回溯-以祖父节点为起点 node = gparent; continue; } /** * case 2: 叔叔是黑色,且当前节点是左孩子--不在一条斜线上 * 1.以父亲为支点-右旋 * 2.把原来的父亲节点标记为旋转后的当前节点 * 3.把原来的当前节点标记为旋转后的父亲节点 * case2 的情况 转变为 case 3 */ if(parent.left == node){ RBTreeNode<T> tmp; rightRotate(parent); tmp = parent; parent = node; node = tmp; } /** * case 3: 叔叔是黑色,且当前节点是右孩子--祖父节点、父节点、子节点在一条斜线上 * 1.将父节点设为黑 * 2.将祖父节点设为红 * 3.以祖父节点为支点-左旋 */ setBlack(parent); setRed(gparent); leftRotate(gparent); } } //将根节点设为黑色 setBlack(root); } /** * 删除节点 * @param node * 后继节点相当于替身,只复制内容不拷贝颜色; * Copy后继节点后再将后继节点删除,这样就巧妙的将问题转换为"删除后继节点"的情况了 */ private void remove(RBTreeNode<T> node){ RBTreeNode<T> parent; RBTreeNode<T> child; boolean color; //待删除节点 左右孩子 都不为空的情况 if(node.left != null && node.right != null){ /** * 被删节点的后继节点。(称为"取代节点") * 用它来取代"被删节点"的位置,然后再将"被删节点"去掉 */ RBTreeNode<T> replace = node; //获取后继节点--该节点右孩子的左后代 replace = node.right; while(replace.left != null) replace = replace.left; //node节点不是根节点 if(parentOf(node) != null){ /** * 1.用后继节点取代删除节点 */ if(parentOf(node).left == node){ node.parent.left = replace; } else{ node.parent.right = replace; } }else{ //待删节点为根节点 root = replace; } //child 是后继节点的右孩子 //parent 是后继节点的父节点 //color 后继节点的颜色 child = replace.right; parent = parentOf(replace); /*小心 有意思 2018/01/02*/ color = colorOf(replace); //待删节点 是 它后继节点的父节点 if(parent == node){ parent = replace; }else{ /** * 若后继节点的右子节点不为空 * 2.把后继节点的右子节点指向后继父节点的左孩子 */ if(child != null) setParent(child, parent); parent.left = child; /** * 3.将待删节点的右孩子指向后继节点的右孩子 */ replace.right = node.right; setParent(node.right, replace); } /** * 4.将待删节点的左孩子置为后继节点的左孩子 */ replace.parent = node.parent; /** * 高能,小心 有雷!! * 保留待删节点原来的颜色 * 只Copy后继节点内容,不Copy后继节点颜色!!! * 2018/01/02 */ replace.color = node.color; replace.left = node.left; node.left.parent = replace; /** * 如果删除了黑色节点,则需要调整 * 根据后继节点的颜色来判断是否调整 * 因为上面两个子节点删除时,只是copy后继节点的值,保留了待删节点的颜色 */ if(color == BLACK) removeFixUp(child, parent); node = null; return; } /** * 待删节点只有一个孩子 甚至 没有孩子 */ if(node.left != null){ child = node.left; }else{ child = node.right; } parent = node.parent; //保存 待删节点 的颜色 color = node.color; if(child != null) child.parent = parent; //node节点不是根节点 if(parent != null){ if(parent.left == node) parent.left = child; else parent.right = child; }else{ root = child; } /** * 如果删除了黑色节点,则需要调整 */ if(color == BLACK) removeFixUp(child, parent); node = null; } public void remove(T key){ RBTreeNode<T> node; if((node = search(key)) != null) remove(node); } /** * 红黑树删除修正函数 * 针对删除黑色节点后才有的修正行为 * @param node 待修正的节点 * @param parent * 总体思想: * 从兄弟节点借调黑色节点使树保持局部的平衡 * 如果兄弟节点没有黑色节点可以借调的话,就只能往上追溯, * 将每一级的黑色节点数减去一个,使整棵树符合定义 */ private void removeFixUp(RBTreeNode<T> node, RBTreeNode<T> parent){ RBTreeNode<T> brother; //待调整节点的兄弟节点 while((node != null || isBlack(node)) && node != root){ if(parent.left == node){ //待调整节点为左孩子 brother = parent.right; /** * case 1: 兄弟是红色的 */ if(isRed(brother)){ setBlack(brother); setRed(parent); leftRotate(brother); brother = parent.right; } /** * case 2: 兄弟是黑色的,且兄弟两个孩子也都是黑色的 */ if((brother.left == null || isBlack(brother.left)) && (brother.right == null || isBlack(brother.right))){ setRed(brother); node = parent; parent = parentOf(node); }else{ /** * case 3: 兄弟是黑色的,兄弟的左孩子是红色,右孩子黑色 */ if(brother.right == null || isBlack(brother.right)){ setBlack(brother.left); setRed(brother); rightRotate(brother); brother = parent.right; } /** * case 4: 兄弟节点是黑色的,并且兄弟右孩子是红色,兄弟节点左孩子任意颜色 */ //把父节点的颜色赋给兄弟节点 setColor(brother, colorOf(parent)); setBlack(parent); setBlack(brother.right); leftRotate(parent); /** * WTF ? * 让x指向根节点,表示调整结束? */ node = root; break; } }else{ //待调整节点为右孩子 brother = parent.left; /** * case 1: 兄弟是红色的 */ if(isRed(brother)){ setBlack(brother); setRed(parent); rightRotate(brother); brother = parent.left; } /** * case 2: 兄弟是黑色的,且兄弟两个孩子也都是黑色的 */ if((brother.left == null || isBlack(brother.left)) && (brother.right == null || isBlack(brother.right))){ setRed(brother); node = parent; parent = parentOf(node); }else{ /** * case 3: 兄弟是黑色的,兄弟的左孩子是红色,右孩子黑色 */ if(brother.left == null || isBlack(brother.left)){ setBlack(brother.right); setRed(brother); leftRotate(brother); brother = parent.left; } /** * case 4: 兄弟节点是黑色的,并且兄弟右孩子是红色,兄弟节点左孩子任意颜色 */ //把父节点的颜色赋给兄弟节点 setColor(brother, colorOf(parent)); setBlack(parent); setBlack(brother.left); rightRotate(parent); /** * WTF ? * 让x指向根节点,表示调整结束? */ node = root; break; } } } if(node != null) setBlack(node); } /** * 销毁红黑树 * @param node */ private void destory(RBTreeNode<T> node){ if(node == null) return; if(node.left != null) destory(node.left); if(node.right != null) destory(node.right); node = null; } public void clear(){ destory(root); root = null; } /** * 打印红黑树 * @param node * @param key * @param direction -- 0 根节点 * -1 左孩子 * 1 右孩子 */ private void print(RBTreeNode<T> node, T key, int direction){ if(node != null){ if(direction == 0){ System.out.printf("%2d(B) is root\n", node.key); }else{ System.out.printf("%2d(%s) is %2d's %6s child\n", node.key, isRed(node)?"R":"B", key, direction==1 ? "right" : "left"); } print(node.left, node.key, -1); print(node.right, node.key, 1); } } public void print(){ if(root != null) print(root, root.key, 0); } public void display(){ inOrder(); } public void test(T key){ RBTreeNode<T> node = search(key); System.out.println(node); } /** * debug method * every layer output in one line * 按层打印 */ public void printTree(){ if(root == null){ System.out.println("-->Null Tree."); return; } java.util.LinkedList<RBTreeNode<T>> queue =new java.util.LinkedList<RBTreeNode<T>>(); java.util.LinkedList<RBTreeNode<T>> queue2 =new java.util.LinkedList<RBTreeNode<T>>(); queue.add(root); boolean firstQueue = true; while(!queue.isEmpty() || !queue2.isEmpty()){ java.util.LinkedList<RBTreeNode<T>> q = firstQueue ? queue : queue2; RBTreeNode<T> n = q.poll(); //获取并移除队首元素 if(n != null){ String pos = n.parent == null ? "" : (n == n.parent.left ? "LE":"RI"); String pstr = n.parent == null ? "" : n.parent.toString(); String cstr = isRed(n) ? "R":"B"; cstr = n.parent == null ? cstr : cstr + " "; System.out.print(n+"("+(cstr)+pstr+(pos)+")"+"\t"); if(n.left != null){ (firstQueue ? queue2 : queue).add(n.left); } if(n.right != null){ (firstQueue ? queue2 : queue).add(n.right); } }else{ System.out.println(); firstQueue = !firstQueue; } } } }
测试类/** * @author yangw * @version V1.0 * @Date 2018年1月1日 下午3:08:11 * @Email * @Description: 红黑树测试类 */ public class RBTreeApp { private static final int a[] = {10, 40, 30, 60, 90, 70, 20, 50, 80}; private static final boolean mDebugInsert = false; // "插入"动作的检测开关(false,关闭;true,打开) private static final boolean mDebugDelete = false; // "删除"动作的检测开关(false,关闭;true,打开) public static void main(String[] args) { int i, ilen = a.length; RBTree<Integer> tree=new RBTree<Integer>(); System.out.printf("== 原始数据: "); for(i=0; i<ilen; i++) System.out.printf("%d ", a[i]); System.out.printf("\n"); for(i=0; i<ilen; i++) { tree.insert(a[i]); // 设置mDebugInsert=true,测试"添加函数" if (mDebugInsert) { System.out.printf("== 添加节点: %d\n", a[i]); System.out.printf("== 树的详细信息: \n"); tree.print(); System.out.printf("\n"); } } /* System.out.printf("== 前序遍历: "); tree.preOrder(); System.out.printf("\n== 中序遍历: "); tree.inOrder(); System.out.printf("\n== 后序遍历: "); tree.postOrder(); */ /*插入信息显示 System.out.printf("== 树的详细信息: \n"); // tree.print(); tree.printTree(); System.out.printf("\n"); */ /* // 设置mDebugDelete=true,测试"删除函数" if (mDebugDelete) { for(i=0; i<ilen; i++){ tree.remove(a[i]); System.out.printf("== 删除节点: %d\n", a[i]); System.out.printf("== 树的详细信息: \n"); tree.print(); System.out.printf("\n"); } } */ /*删除测试*/ System.out.printf("== 删除节点 测试: \n"); //1.删除叶子节点 //tree.remove(50); //2.删除带有一个子节点的节点 // tree.remove(10); //3.删除带有两个子节点的节点 tree.remove(30); tree.printTree(); // tree.test(70); //销毁二叉树 tree.clear(); } }
-
-
-