remove方法可以分为两个步骤,先是找到这个节点,直接调用了上面介绍的getEntry(Object key),这个步骤我们就不说了,直接说第二个步骤,找到后的删除操作。
public V remove(Object key) { Entry<K,V> p = getEntry(key); if (p == null) return null; V oldValue = p.value; deleteEntry(p); return oldValue; }通过deleteEntry(p)进行删除操作,删除操作的原理我们在前面已经讲过
删除的是根节点,则直接将根节点置为null;
待删除节点的左右子节点都为null,删除时将该节点置为null;
待删除节点的左右子节点有一个有值,则用有值的节点替换该节点即可;
待删除节点的左右子节点都不为null,则找前驱或者后继,将前驱或者后继的值复制到该节点中,然后删除前驱或者后继(前驱:左子树中值最大的节点,后继:右子树中值最小的节点);
private void deleteEntry(Entry<K,V> p) { modCount++; size--; //当左右子节点都不为null时,通过successor(p)遍历红黑树找到前驱或者后继 if (p.left != null && p.right != null) { Entry<K,V> s = successor(p); //将前驱或者后继的key和value复制到当前节点p中,然后删除节点s(通过将节点p引用指向s) p.key = s.key; p.value = s.value; p = s; } Entry<K,V> replacement = (p.left != null ? p.left : p.right); /** * 至少有一个子节点不为null,直接用这个有值的节点替换掉当前节点,给replacement的parent属性赋值,给 * parent节点的left属性和right属性赋值,同时要记住叶子节点必须为null,然后用fixAfterDeletion方法 * 进行自平衡处理 */ if (replacement != null) { //将待删除节点的子节点挂到待删除节点的父节点上。 replacement.parent = p.parent; if (p.parent == null) root = replacement; else if (p == p.parent.left) p.parent.left = replacement; else p.parent.right = replacement; p.left = p.right = p.parent = null; /** * p如果是红色节点的话,那么其子节点replacement必然为红色的,并不影响红黑树的结构 * 但如果p为黑色节点的话,那么其父节点以及子节点都可能是红色的,那么很明显可能会存在红色相连的情 * 况,因此需要进行自平衡的调整 */ if (p.color == BLACK) fixAfterDeletion(replacement); } else if (p.parent == null) {//这种情况就不用多说了吧 root = null; } else { /** * 如果p节点为黑色,那么p节点删除后,就可能违背每个节点到其叶子节点路径上黑色节点数量一致的规则, * 因此需要进行自平衡的调整 */ if (p.color == BLACK) fixAfterDeletion(p); if (p.parent != null) { if (p == p.parent.left) p.parent.left = null; else if (p == p.parent.right) p.parent.right = null; p.parent = null; } } }操作的操作其实很简单,场景也不多,我们看一下删除后的自平衡操作方法fixAfterDeletion
private void fixAfterDeletion(Entry<K,V> x) { /** * 当x不是root节点且颜色为黑色时 */ while (x != root && colorOf(x) == BLACK) { /** * 首先分为两种情况,当前节点x是左节点或者当前节点x是右节点,这两种情况下面都是四种场景,这里通过 * 代码分析一下x为左节点的情况,右节点可参考左节点理解,因为它们非常类似 */ if (x == leftOf(parentOf(x))) { Entry<K,V> sib = rightOf(parentOf(x)); /** * 场景1:当x是左黑色节点,兄弟节点sib是红色节点 * 兄弟节点由红转黑,父节点由黑转红,按父节点左旋, * 左旋后树的结构变化了,这时重新赋值sib,这个时候sib指向了x的兄弟节点 */ if (colorOf(sib) == RED) { setColor(sib, BLACK); setColor(parentOf(x), RED); rotateLeft(parentOf(x)); sib = rightOf(parentOf(x)); } /** * 场景2:节点x、x的兄弟节点sib、sib的左子节点和右子节点都为黑色时,需要将该节点sib由黑变 * 红,同时将x指向当前x的父节点 */ if (colorOf(leftOf(sib)) == BLACK && colorOf(rightOf(sib)) == BLACK) { setColor(sib, RED); x = parentOf(x); } else { /** * 场景3:节点x、x的兄弟节点sib、sib的右子节点都为黑色,sib的左子节点为红色时, * 需要将sib左子节点设置为黑色,sib节点设置为红色,同时按sib右旋,再将sib指向x的 * 兄弟节点 */ if (colorOf(rightOf(sib)) == BLACK) { setColor(leftOf(sib), BLACK); setColor(sib, RED); rotateRight(sib); sib = rightOf(parentOf(x)); } /** * 场景4:节点x、x的兄弟节点sib都为黑色,而sib的左右子节点都为红色或者右子节点为红色、 * 左子节点为黑色,此时需要将sib节点的颜色设置成和x的父节点p相同的颜色, * 设置x的父节点为黑色,设置sib右子节点为黑色,左旋x的父节点p,然后将x赋值为root */ setColor(sib, colorOf(parentOf(x))); setColor(parentOf(x), BLACK); setColor(rightOf(sib), BLACK); rotateLeft(parentOf(x)); x = root; } } else {//x是右节点的情况 Entry<K,V> sib = leftOf(parentOf(x)); if (colorOf(sib) == RED) { setColor(sib, BLACK); setColor(parentOf(x), RED); rotateRight(parentOf(x)); sib = leftOf(parentOf(x)); } if (colorOf(rightOf(sib)) == BLACK && colorOf(leftOf(sib)) == BLACK) { setColor(sib, RED); x = parentOf(x); } else { if (colorOf(leftOf(sib)) == BLACK) { setColor(rightOf(sib), BLACK); setColor(sib, RED); rotateLeft(sib); sib = leftOf(parentOf(x)); } setColor(sib, colorOf(parentOf(x))); setColor(parentOf(x), BLACK); setColor(leftOf(sib), BLACK); rotateRight(parentOf(x)); x = root; } } } setColor(x, BLACK); }