首先,在阅读文章以前,我但愿读者对二叉树有必定的了解,由于红黑树的本质就是一颗二叉树。因此本篇博客中不在将二叉树的增删查的基本操做了,须要了解的同窗能够到我以前写的一篇关于二叉树基本操做的博客:http://www.javashuo.com/article/p-vdgloopa-gr.html;html
有随机数节点组成的二叉树的平均高度为logn,因此正常状况下二叉树查找的时间复杂度为O(logn)。可是,根据二叉树的特性,在最坏的状况下,好比存储的是一个有序的数据的话,那么因此的数据都会造成一条链,此时二叉树的深度为n,时间复杂度为O(n)。红黑树就是为了解决这个问题的,它可以保证在任何状况下树的深度都保持在logn左右,红黑树经过一下约束来完成这个特性:node
一、每一个节点不是红色就是黑色。学习
二、根节点为黑色。this
三、每一个叶子节点都是黑色的。spa
四、每一个红色节点的子节点都是黑色。3d
五、任意节点,到其任意叶节点的全部路径都包含相同的黑色节点。指针
结构以下图:code
红黑树的基本操做包括删除和添加。在删除或者添加一个节点的时候就有可能打破原有的红黑树维持的平衡,那么就须要经过着色和旋转的方式来使红黑树从新达到平衡。着色是很是简单的,直接将节点的颜色改变就能够了,多以要理解红黑树,就必须须要懂得如何进行旋转,旋转又分为左旋和右转,两个操做相反的,因此理解了一个旋转的操做就很容易理解另外一个旋转了。视频
左旋:htm
如图所示,红色节点为旋转支点,支点往左子树移动即为左旋。左旋以后咱们能够看到原支点的位置被原支点的右子节点代替,新支点的左子节点变为了原来为父节点的原支点,新支点的左子节点变为原支点的右子节点,所以左旋操做总共右3个节点,觉得旋转前的结构举例,分别为红色节点(原支点),黄色节点(新支点)和L节点。Java代码实现以下:
/** * 左旋 * @param e 支点 */ private void leftRotate(Entry<K,V> e){ //支点的右子节点 Entry<K,V> right = e.right; //支点右子节点的左子节点 Entry<K,V> rightOfLeft = right.left; //新旧支点的替换 right.parent = e.parent; if (e.parent == null){ root = right; }else { if (e == e.parent.left) e.parent.left = right; else e.parent.right = right; } //将原支点变为新支点的左节点 right.left = e; e.parent = right; //将新支点的左节点变为就支点的右节点 e.right = rightOfLeft; if (rightOfLeft != null) rightOfLeft.parent = e; }
由于在红黑树中每一个节点都有一个指针指向本身的父节点,父节点也有指针指向子节点,由于在改动一个节点的时候都须要分别改动当前节点和父节点的指向,结合左旋的示意图,用Java代码实现起来就不会很困难了。
右旋
右旋操做和左旋相反的,二者互反。依然是红色做为旋转支点,右旋后黄色节点代替了红色节点原来的位置,黄色节点的右节点旋转后变为红色节点的左节点。Java 代码实现以下:
/** * 右旋 * @param e 旋转支点 */ private void rightRotate(Entry<K,V> e){ //原支点的左节点 Entry<K,V> left = e.left; //原支点的左节点的右节点 Entry<K,V> leftOfRight = left.right; //新旧支点的替换 left.parent = e.parent; if (e.parent == null){//支点的父节点为根节点的状况 root = left; }else {//非跟节点 if (e == e.parent.left) e.parent.left = left; else e.parent.right = left; } //将原支点变为新支点的右节点 left.right = e; e.parent = left; //将新支点未旋转前的右节点变为转换后的原支点的左节点 e.left = leftOfRight; if (leftOfRight != null) leftOfRight.parent = e; }
添加节点
首先,在进入主题以前咱们再来回顾一下红黑树的5个特色:
一、每一个节点不是红色就是黑色。
二、根节点为黑色。
三、每一个叶子节点都是黑色的。
四、每一个红色节点的子节点都是黑色。
五、任意节点,到其任意叶节点的全部路径都包含相同的黑色节点。
红黑树插入节点与二叉树是一致的,因此每次添加节点确定是添加到叶子节点上,具体步骤以下:
第一步:将新节点插入到红黑树中。
第二步:将新节点设置为红色。这里为何须要设置成红色呢?主要是为了知足特性5,这样在插入节点后就少解决了一个冲突,也就少一点麻烦。插入完成后,咱们来看一下还有那些特性是有可能发生冲突的,特性1每一个节点不是红色就是黑色的,这明显没有冲突,特性2根节点为黑色,当插入节点为根节点的时候就会有冲突了,这种就很简单了,直接将根节点着色为黑色便可。特性3每一个叶子节点都是黑色,这个明显没有冲突。特性4每一个红色节点的子节点都是黑色的,这个特性就有可能会冲突,由于在插入新节点的时候咱们没法肯定新节点的父节点的颜色是黑色的仍是红色,若是新节点的父节为黑色,那么就不会有冲突,不然就会违背了特性4。特性5任意节点,到其任意子节点的全部路径都包含相同的黑色节点,由于咱们插入的新节点被着色为红色,因此并不会影响到每一个路径的黑色节点的数量,所以也不会有冲突。综上所诉,那么在插入新节点的时候,只有特性4有可能发生冲突。
第三步:平衡红黑树,使之成为新的红黑树。
根据第二部获得的结论,咱们能够知道只有状况是须要解决冲突的,那就是新节点的父节点为红色的时候违背了特性4。接下来咱们将要讨论这个问题,由于在新插入一个节点以前是一颗已经平衡了的红黑树,所以根据特新4,新节点的祖父节点一定为黑色。根据这种状况,咱们又能够分为如下四种状况:
状况1:新节点为左节点,叔叔节点为红色;
状况2:新节点为左节点,叔叔节点为黑色;
状况3:新节点为右节点,叔叔节点为红色;
状况4:新节点为右节点,叔叔节点为黑色;
状况1和状况3的状况是同样的,因此咱们能够将这两种状况看做是一种状况,这个状况咱们稍后再讨论,而后看一下状况2和状况4,经过左旋就能够转换成状况2。
综上所述,咱们能够归结为3中状况:
状况1:叔叔节点是红色节点;
状况2:叔叔节点是黑色节点,新节点为右节点;
状况3:叔叔节点是黑色节点,新节点为左节点;
上面我也有提到,当插入新节点时确定是属于第一种状况的,而后二、3由1转换而来,在此以前我但愿你以前已经了解过递归的原理和思想,把局部看做总体的思想,由于这将有助于下面讨论的理解。下面咱们将要继续分析这三种状况,状况1这种状况处理起来比比较简单,只须要将祖父节点变为红色节点,父节点和叔叔节点变为黑色便可,这仅仅只是当整个红黑树只有这几个节点的时候是能够了,但事实并不是如此,这仅仅只是达到了局部平衡。
上图,咱们看到已经达到了局部的平衡,可是,咱们还会有其余的状况,那就是祖父节点有可能也会有父节点。那么又会有两种状况,1是祖父节点的父节点多是黑色的,2是多是红色的,若是黑色那么整个红黑树就达到平衡了。不知道你们根觉到了没有,这两种状况是否是跟新插入一个节点的状况是一致的,是否是又回到了插入新节点的问题了?因而我将局部收到影响的部分画出来,如图:
图a就是将状况1重新着色后的部分受影响的节点,固然只是其中的一种状况,此时咱们将已经平衡的部分去掉就变成的图b的状况,这种状况是否是很熟悉呢?咱们的祖父节点当成新节点,是否是至关于上面讨论的状况1呢?不过与上面讨论的状况不一样的是,这里3中可能状况均可能出现,由于叔叔节点有可能为红色或黑色。因此这时候才有可能出现真正的三种状况:
状况1:叔叔节点是红色节点;
状况2:叔叔节点是黑色节点,新节点为右节点;
状况3:叔叔节点是黑色节点,新节点为左节点;
若是为状况1的话,咱们一层一层的往上平衡就能够了,当祖父节点为根节点的时候,咱们直接将根节点着色为黑色便可,由于祖父节点的两个子节点都是黑色的,因此变为黑色后仍然是平衡的。接下来咱们来讨论下状况2和3。
很明显的,这两种状况的右节点多出了一个黑色节点,这种状况是在状况1向上着色的时候形成的,即祖父节点由黑色节点变为了红色节点。状况2以父节点为支点左旋,而后将父节点和新节点互换能够获得状况3:
状况3进行的操做是,首先将父节点着色为黑色,祖父节点着色为红色,而后以祖父为支点进行右旋
状况3旋转结束后整棵红黑也已经从新恢复平衡了。单从部分其实并看不出已经平衡了,咱们能够将三个状况连起来就能够看到了,以下图:
上图中都是以n节点为参考点的,其他无关的节点就不标出来了。n节点即为插入节点,可是除了第一次操做n节点为真正的新节点,此后的操做所指的n节点只是有助于咱们的理解把他当成新节点。固然,这只是其中的一种状况,其余其余的状况能够经过不断向上旋转或着色,最终也会达到这种状况或者顶部是p节点为根节点的时候,第二种状况直接将根节点着色为黑色便可。
总结:
回顾一下红黑树的5个特性:
一、节点不是红色就是黑色。
二、根节点为黑色。
三、叶子节点为黑色。
四、每一个红色节点其子节点必须是黑色节点。
五、任意节点到到其任意的子节点的全部路径的黑色节点的数量相等。
在插入新节点的时候很显然,不会违背1和3,若是插入的是根节点直接将根节点着色为黑色便可,这种状况能够忽略不计,因此插入节点时可能会违背了4和5,又由于插入的是红色节点所以5也不会违背,最后在插入新节点的时候咱们只须要关注特性4就能够了。当父节点为红色的时候跟4有冲突,因此咱们接下来讨论的就是这种状况。咱们知道,在插入新节点以前整颗红黑树是平衡的,所以能够得出一个结论就是祖父节点确定确定是黑色的。咱们如今只关注相关的节点便可,目前,咱们知道了祖父的节点为黑色,父节点为红色,可是叔叔节点的颜色不知道,新节点的位置也不能肯定,因此有2x2中状况,当叔叔节点为红色的时候,两种状况的处理方式是一致的,因此最后咱们能够总结为3中状况:
一、叔叔节点为红色
二、新节点为右节点,叔叔节点为黑色
三、新节点为左节点,叔叔节点为黑色
类型 | 描述 | 步骤 | 示意图 |
状况1 | 叔叔节点为红色 | 一、父节点设为黑色 二、叔叔节点设为黑色 三、祖父节点设为红色 四、把祖父节点设置为新节点(当前节点) |
![]()
|
状况2 | 新节点为右节点,叔叔节点为黑色 | 一、以父节点为支点左旋 二、父节点和新节点互换位置 三、把父节点设为当前节点 |
![]()
|
状况3 | 新节点为左节点,叔叔节点为黑色 | 一、父节点设为黑色 二、祖父节点设为红色 三、以祖父节点为支点右旋 |
![]()
|
整合 | 树a就是表中的状况1,经过着色后直接转换成了状况3,状况3进行着色旋转后达到了平衡,当树b中的叔叔节点为红色的时候与树a一致,循环调用树a的处理方式,直至达到树b的状况或者树a中的祖父节点到达了根节点,这时候将祖父节点设为黑色便可。 这种状况就是由状况1转状况2再转状况3,由状况3从新着色旋转后达到平衡。 须要注意的是:不是每次插入节点都会出现3中状况,有可能只出现了2和3,或者只出现了3一种状况。 |
上面是讨论左子树的问题,由于红黑色具备堆成性,所以在处理右子树的时候与处理左子树相反便可。Java代码示例以下:
/** * 插入新节点后平衡红黑树 * @param e 新节点 */ private void fixAfterInsertion(Entry<K, V> e) { //将新插入节点设置为红色 setRed(e); Entry<K,V> p,g,u;//父节点和祖父节点和叔叔节点 Entry<K,V> current = e;//新节点 /** * 这里经过循环不断向上平衡 */ while ((p = parentOf(current)) != null && isRed(p)){ g = parentOf(p);//祖父节点 if (p == g.left){ u = g.right; //状况1:叔叔节点为红色 if (u != null && isRed(u)){ setBlack(p);//父节点设为黑色 setBlack(u);//叔叔节点设为黑色 setRed(g);//祖父节点设为红色 current = g;//把祖父节点设为当前节点 //继续向上平衡 continue; } //状况2:当前节点为右节点,叔叔节点为黑色 if (current == p.right){ leftRotate(p);//父节点为支点左旋 Entry<K,V> tmp = p; p = current;//父节点和当前节点互换 current = tmp;//父节点设为当前节点 } //状况3:当前节点为左节点,叔叔节点为黑色 setBlack(p);//父节点设为黑色 setRed(g);//祖父节点设为红色 rightRotate(g);//祖父节点为支点右旋 }else {//相反的操做 u = g.left; if (u != null && isRed(u)){ setBlack(p); setBlack(u); setRed(g); current = g; continue; } if (current == p.left){ rightRotate(p); Entry<K,V> tmp = p; p = current; current = tmp; } setBlack(p); setRed(g); leftRotate(g); } } //最后将根节点设置为红色 setBlack(root); }
删除节点
在二叉树分析一文中已经说过,删除一个节点的时候有3中状况:
一、删除节点没有子节点
二、删除节点只有一个子节点
三、删除节点有两个子节点
首先,咱们逐个来分析每种状况删除节点后对整颗红黑树的平衡性的影响。在删除节点时候红黑树的特性1,2,3确定不会违背,因此只须要考虑特性4,5便可。
对于状况1,确定不会违背特性4,若是删除节点为红色,那么对整颗红黑树的平衡性都不会影响,若是是黑色则违背了特性5,咱们先将这种状况记录下来,稍后再进一步讨论。
对于状况2,有可能删除的是左子树或右子树,暂且不讨论。若是删除的节点为红色,不影响平衡性,若是删除的是黑色,那么确定会和特性5有冲突,当删除节点的父节点为红色,子节点为红色是也和特性4有冲突。
对于状况3,其实最后删除的是它的替代节点,根据替代节点的特色,最终实际上是回到了1这种状况或者状况2。
总结上面的3种状况可获得一个结论,只有删除节点为黑色时才会破坏红黑树原来的平衡,因在删除节点以前红黑树是出于平衡状态的,删除以后很明显的其兄弟节点分支必然比删除节点的分支多了一个黑色的节点,所以咱们只须要改变兄弟节点的颜色便可,咱们只讨论左节点,右节点对称。
1、删除节点的兄弟节点是红色
将兄弟节点设为黑色,父节点设为红色,以父节点为支点左旋转,而后将父节点的右节点放到兄弟节点上:
2、兄弟节点是黑色的,兄弟的两个子节点也都是黑色的
兄弟节点设为红色,把父节点设置为新的删除节点:
3、兄弟节点是黑色的,且兄弟节点的左子节点是红色,右子节点是黑色
将兄弟节点的左子节点设为黑色,兄弟节点设为红色,以兄弟节点为支点右旋,把父节点的右节点设置为兄弟节点
4、兄弟节点是黑色的,且兄弟节点的右子节点是红色,左子节点任意颜色
把兄弟节点的设为父节点的颜色,父节点设为黑色,父节点的右节点设为黑色,父节点为支点左旋
删除的Java代码示例:
public V remove(Object key){ if (key == null) return null; Entry<K,V> delEntry; delEntry = getEntry(key); if (delEntry == null) return null; size--; Entry<K,V> p = delEntry.parent; if (delEntry.right == null && delEntry.left == null){ if (p == null){ root = null; }else { if (p.left == delEntry){ p.left = null; }else { p.right = null; } } }else if (delEntry.right == null){//只有左节点 Entry<K,V> lc = delEntry.left; if (p == null) { lc.parent = null; root = lc; } else { if (delEntry == p.left){ p.left = lc; }else { p.right = lc; } lc.parent = p; } }else if (delEntry.left == null){//只有右节点 Entry<K,V> rc = delEntry.right; if (p == null) { rc.parent = null; root = rc; }else { if (delEntry == p.left) p.left = rc; else p.right = rc; rc.parent = p; } }else {//有两个节点,找到后继节点,将值赋给删除节点,而后将后继节点删除掉便可 Entry<K,V> successor = successor(delEntry);//获取到后继节点 boolean color = successor.color; V old = delEntry.value; delEntry.value = successor.value; delEntry.key = successor.key; if (delEntry.right == successor){//后继节点为右子节点, if (successor.right != null) {//右子节点有右子节点 delEntry.right = successor.right; successor.right.parent = delEntry; }else {//右子节点没有子节点 delEntry.right = null; } }else { successor.parent.left = null; } if (color == BLACK) //fixUpAfterRemove(child,parent); return old; } V old = delEntry.value; if (delEntry.color == BLACK)//删除为黑色时,须要从新平衡树 if (delEntry.right != null)//删除节点的子节点只有右节点 fixUpAfterRemove(delEntry.right,delEntry.parent); else if (delEntry.left != null)//删除节点只有左节点 fixUpAfterRemove(delEntry.left,delEntry.parent); else fixUpAfterRemove(null,delEntry.parent); delEntry.parent = null; delEntry.left = null; delEntry.right = null; return old; } private Entry<K, V> getEntry(Object key) { if (key == null) return null; Entry<K, V> delEntry = null; Entry<K, V> current = root; int ret; if (comparator == null){ Comparable<K> k = (Comparable<K>) key; while (current != null){ ret = k.compareTo(current.key); if (ret < 0) current = current.left; else if (ret > 0) current = current.right; else{ delEntry = current; break; } } }else { for (;current != null;){ ret = comparator.compare(current.key, (K) key); if (ret < 0) current = current.left; else if (ret > 0) current = current.right; else{ delEntry = current; break; } } } return delEntry; } //node表示待修正的节点,即后继节点的子节点(由于后继节点被挪到删除节点的位置去了) private void fixUpAfterRemove(Entry<K, V> node,Entry<K,V> parent) { Entry<K,V> other; while((node == null || isBlack(node)) && (node != root)) { if(parent.left == node) { //node是左子节点,下面else与这里的恰好相反 other = parent.right; //node的兄弟节点 if(isRed(other)) { //case1: node的兄弟节点other是红色的 setBlack(other); setRed(parent); leftRotate(parent); other = parent.right; } //case2: node的兄弟节点other是黑色的,且other的两个子节点也都是黑色的 if((other.left == null || isBlack(other.left)) && (other.right == null || isBlack(other.right))) { setRed(other); node = parent; parent = parentOf(node); } else { //case3: node的兄弟节点other是黑色的,且other的左子节点是红色,右子节点是黑色 if(other.right == null || isBlack(other.right)) { setBlack(other.left); setRed(other); rightRotate(other); other = parent.right; } //case4: node的兄弟节点other是黑色的,且other的右子节点是红色,左子节点任意颜色 setColor(other, colorOf(parent)); setBlack(parent); setBlack(other.right); leftRotate(parent); node = this.root; break; } } else { //与上面的对称 other = parent.left; if (isRed(other)) { // Case 1: node的兄弟other是红色的 setBlack(other); setRed(parent); rightRotate(parent); other = parent.left; } if ((other.left==null || isBlack(other.left)) && (other.right==null || isBlack(other.right))) { // Case 2: node的兄弟other是黑色,且other的俩个子节点都是黑色的 setRed(other); node = parent; parent = parentOf(node); } else { if (other.left==null || isBlack(other.left)) { // Case 3: node的兄弟other是黑色的,而且other的左子节点是红色,右子节点为黑色。 setBlack(other.right); setRed(other); leftRotate(other); other = parent.left; } // Case 4: node的兄弟other是黑色的;而且other的左子节点是红色的,右子节点任意颜色 setColor(other, colorOf(parent)); setBlack(parent); setBlack(other.left); rightRotate(parent); node = this.root; break; } } } if (node!=null) setBlack(node); } private Entry<K, V> successor(Entry<K, V> delEntry) { Entry<K,V> r = delEntry.right;//assert r != null; while (r.left != null){ r = r.left; } return r; }
完整的代码示例:
public class MyTreeMap<K,V> { private static final boolean BLACK = true; private static final boolean RED = false; private Entry<K,V> root; private int size = 0; private final Comparator<K> comparator; MyTreeMap(){ comparator =null; } public MyTreeMap(Comparator comparator){ this.comparator = comparator; } public V put(K key,V value){ if (root == null){ root = new Entry<>(key,value,null); size++; return null; }else { int ret = 0; Entry<K,V> p = null; Entry<K,V> current = root; if (comparator == null){ if (key == null) throw new NullPointerException("key = null"); Comparable<K> k = (Comparable<K>) key; while (current != null){ p =current; ret = k.compareTo(current.key); if (ret < 0) current = current.left; else if(ret > 0) current = current.right; else { current.value = value; return current.value; } } }else { do { p = current; ret = comparator.compare(key,current.key); if (ret < 0) current = current.left; else if (ret > 0) current = current.right; else { current.value = value; return value; } }while (current != null); } Entry<K,V> e = new Entry<>(key,value,p); if (ret < 0) p.left = e; else p.right = e; size++; fixAfterInsertion(e); return e.value; } } /** * 插入新节点后平衡红黑树 * @param e 新节点 */ private void fixAfterInsertion(Entry<K, V> e) { //将新插入节点设置为红色 setRed(e); Entry<K,V> p,g,u;//父节点和祖父节点和叔叔节点 Entry<K,V> current = e;//新节点 /** * 这里经过循环不断向上平衡 */ while ((p = parentOf(current)) != null && isRed(p)){ g = parentOf(p);//祖父节点 if (p == g.left){ u = g.right; //状况1:叔叔节点为红色 if (u != null && isRed(u)){ setBlack(p);//父节点设为黑色 setBlack(u);//叔叔节点设为黑色 setRed(g);//祖父节点设为红色 current = g;//把祖父节点设为当前节点 //继续向上平衡 continue; } //状况2:当前节点为右节点,叔叔节点为黑色 if (current == p.right){ leftRotate(p);//父节点为支点左旋 Entry<K,V> tmp = p; p = current;//父节点和当前节点互换 current = tmp;//父节点设为当前节点 } //状况3:当前节点为左节点,叔叔节点为黑色 setBlack(p);//父节点设为黑色 setRed(g);//祖父节点设为红色 rightRotate(g);//祖父节点为支点右旋 }else {//相反的操做 u = g.left; if (u != null && isRed(u)){ setBlack(p); setBlack(u); setRed(g); current = g; continue; } if (current == p.left){ rightRotate(p); Entry<K,V> tmp = p; p = current; current = tmp; } setBlack(p); setRed(g); leftRotate(g); } } //最后将根节点设置为红色 setBlack(root); } public boolean containsKey(Object key){ return getEntry(key) != null; } public Set<Entry<K,V>> entrySet(){ Set<Entry<K,V>> list = new HashSet<>(size + 4); entries(root,list); return list; } private void entries(Entry<K,V> e,Set<Entry<K,V>> list){ if (e != null){ entries(e.left,list); list.add(e); entries(e.right,list); } } public boolean containsValue(V v){ return values().contains(v); } public V get(Object key){ Entry<K, V> entry = getEntry(key); return entry == null ? null : entry.getValue(); } private void setColor(Entry<K,V> e,boolean color){ if (e != null) e.color = color; } private void setRed(Entry<K,V> e){ setColor(e,RED); } private void setBlack(Entry<K,V> e){ setColor(e,BLACK); } private void setParent(Entry<K,V> e,Entry<K,V> p){ if (e != null) e.parent = p; } private boolean isBlack(Entry<K,V> e){ return colorOf(e) == BLACK; } private boolean isRed(Entry<K,V> e){ return !isBlack(e); } private Entry<K,V> parentOf(Entry<K,V> e){ return e == null ? null : e.parent; } private boolean colorOf(Entry<K,V> e){ return e == null ? BLACK : e.color; } /** * 右旋 * @param e 旋转支点 */ private void rightRotate(Entry<K,V> e){ //原支点的左节点 Entry<K,V> left = e.left; //原支点的左节点的右节点 Entry<K,V> leftOfRight = left.right; //新旧支点的替换 left.parent = e.parent; if (e.parent == null){//支点的父节点为根节点的状况 root = left; }else {//非跟节点 if (e == e.parent.left) e.parent.left = left; else e.parent.right = left; } //将原支点变为新支点的右节点 left.right = e; e.parent = left; //将新支点未旋转前的右节点变为转换后的原支点的左节点 e.left = leftOfRight; if (leftOfRight != null) leftOfRight.parent = e; } /** * 左旋 * @param e 支点 */ private void leftRotate(Entry<K,V> e){ //支点的右子节点 Entry<K,V> right = e.right; //支点右子节点的左子节点 Entry<K,V> rightOfLeft = right.left; //新旧支点的替换 right.parent = e.parent; if (e.parent == null){ root = right; }else { if (e == e.parent.left) e.parent.left = right; else e.parent.right = right; } //将原支点变为新支点的左节点 right.left = e; e.parent = right; //将新支点的左节点变为就支点的右节点 e.right = rightOfLeft; if (rightOfLeft != null) rightOfLeft.parent = e; } public int getDeep(){ return deep(root); } private int deep(Entry<K,V> e){ int deep = 0; if (e != null){ int leftDeep = deep(e.left); int rightDeep = deep(e.right); deep = leftDeep > rightDeep ? leftDeep + 1 : rightDeep + 1; } return deep; } public V remove(Object key){ if (key == null) return null; Entry<K,V> delEntry; delEntry = getEntry(key); if (delEntry == null) return null; size--; Entry<K,V> p = delEntry.parent; if (delEntry.right == null && delEntry.left == null){ if (p == null){ root = null; }else { if (p.left == delEntry){ p.left = null; }else { p.right = null; } } }else if (delEntry.right == null){//只有左节点 Entry<K,V> lc = delEntry.left; if (p == null) { lc.parent = null; root = lc; } else { if (delEntry == p.left){ p.left = lc; }else { p.right = lc; } lc.parent = p; } }else if (delEntry.left == null){//只有右节点 Entry<K,V> rc = delEntry.right; if (p == null) { rc.parent = null; root = rc; }else { if (delEntry == p.left) p.left = rc; else p.right = rc; rc.parent = p; } }else {//有两个节点,找到后继节点,将值赋给删除节点,而后将后继节点删除掉便可 Entry<K,V> successor = successor(delEntry);//获取到后继节点 boolean color = successor.color; V old = delEntry.value; delEntry.value = successor.value; delEntry.key = successor.key; if (delEntry.right == successor){//后继节点为右子节点, if (successor.right != null) {//右子节点有右子节点 delEntry.right = successor.right; successor.right.parent = delEntry; }else {//右子节点没有子节点 delEntry.right = null; } }else { successor.parent.left = null; } if (color == BLACK) //fixUpAfterRemove(child,parent); return old; } V old = delEntry.value; if (delEntry.color == BLACK)//删除为黑色时,须要从新平衡树 if (delEntry.right != null)//删除节点的子节点只有右节点 fixUpAfterRemove(delEntry.right,delEntry.parent); else if (delEntry.left != null)//删除节点只有左节点 fixUpAfterRemove(delEntry.left,delEntry.parent); else fixUpAfterRemove(null,delEntry.parent); delEntry.parent = null; delEntry.left = null; delEntry.right = null; return old; } private Entry<K, V> getEntry(Object key) { if (key == null) return null; Entry<K, V> delEntry = null; Entry<K, V> current = root; int ret; if (comparator == null){ Comparable<K> k = (Comparable<K>) key; while (current != null){ ret = k.compareTo(current.key); if (ret < 0) current = current.left; else if (ret > 0) current = current.right; else{ delEntry = current; break; } } }else { for (;current != null;){ ret = comparator.compare(current.key, (K) key); if (ret < 0) current = current.left; else if (ret > 0) current = current.right; else{ delEntry = current; break; } } } return delEntry; } //node表示待修正的节点,即后继节点的子节点(由于后继节点被挪到删除节点的位置去了) private void fixUpAfterRemove(Entry<K, V> node,Entry<K,V> parent) { Entry<K,V> other; while((node == null || isBlack(node)) && (node != root)) { if(parent.left == node) { //node是左子节点,下面else与这里的恰好相反 other = parent.right; //node的兄弟节点 if(isRed(other)) { //case1: node的兄弟节点other是红色的 setBlack(other); setRed(parent); leftRotate(parent); other = parent.right; } //case2: node的兄弟节点other是黑色的,且other的两个子节点也都是黑色的 if((other.left == null || isBlack(other.left)) && (other.right == null || isBlack(other.right))) { setRed(other); node = parent; parent = parentOf(node); } else { //case3: node的兄弟节点other是黑色的,且other的左子节点是红色,右子节点是黑色 if(other.right == null || isBlack(other.right)) { setBlack(other.left); setRed(other); rightRotate(other); other = parent.right; } //case4: node的兄弟节点other是黑色的,且other的右子节点是红色,左子节点任意颜色 setColor(other, colorOf(parent)); setBlack(parent); setBlack(other.right); leftRotate(parent); node = this.root; break; } } else { //与上面的对称 other = parent.left; if (isRed(other)) { // Case 1: node的兄弟other是红色的 setBlack(other); setRed(parent); rightRotate(parent); other = parent.left; } if ((other.left==null || isBlack(other.left)) && (other.right==null || isBlack(other.right))) { // Case 2: node的兄弟other是黑色,且other的俩个子节点都是黑色的 setRed(other); node = parent; parent = parentOf(node); } else { if (other.left==null || isBlack(other.left)) { // Case 3: node的兄弟other是黑色的,而且other的左子节点是红色,右子节点为黑色。 setBlack(other.right); setRed(other); leftRotate(other); other = parent.left; } // Case 4: node的兄弟other是黑色的;而且other的左子节点是红色的,右子节点任意颜色 setColor(other, colorOf(parent)); setBlack(parent); setBlack(other.left); rightRotate(parent); node = this.root; break; } } } if (node!=null) setBlack(node); } private Entry<K, V> successor(Entry<K, V> delEntry) { Entry<K,V> r = delEntry.right;//assert r != null; while (r.left != null){ r = r.left; } return r; } List<V> values(){ List<V> set = new ArrayList<>(size+4); midIterator(root,set); return set; } private void midIterator(Entry<K,V> e, List<V> values){ if (e != null){ midIterator(e.left,values); values.add(e.value); midIterator(e.right,values); } } public void clear(){ clear(root); root = null; } private void clear(Entry<K,V> node) { if (node != null){ clear(node.left); node.left = null; clear(node.right); node.right = null; } } public int size(){return size;} static final class Entry<K,V>{ private K key; private V value; private Entry<K,V> left; private Entry<K,V> right; private Entry<K,V> parent; private boolean color = BLACK; Entry(K key,V value,Entry<K,V> parent){ this.key = key; this.value = value; this.parent = parent; } public K getKey() { return key; } public V getValue() { return value; } } }
到此,红黑树的添加删除操做已经所有讲完了,若是文中有什么错误或不懂得地方,随时欢迎你们指出讨论。你们也能够关注公众号:【Java解忧杂货铺】,里面会不定时得发布一些技术干活,和学习视频。