- 写在前面:记录学习《恋上数据结构与算法》的过程。
- 课程链接地址:https://ke.qq.com/course/385223
目录
删除 - BLACK 叶子节点 - sibling 为BLAC
红黑树(Red Black Tree)
- 下面这棵树是红黑树么?
- 不是。不满足性质5,如节点38的叶子节点。
红黑树的等价变换
红黑树 VS 2-3-4树
几个重要的名词
一些辅助函数
RBTree.java 代码实现
染色
染成红色或黑色
判断颜色
判断为黑色或红色
BinaryTree.java代码实现
判断是左子节点或右子节点
判断是否为兄弟节点
添加
- 由红黑树立即联想到对应的B树
添加的所有情况
LL\RR情况
LR\RL情况
上溢 LL
上溢 RR
上溢 LR
上溢 RL
总结:12种情况
- 四种情况:父节点是黑色,不用处理。
- 八种情况:父节点是红色
- 四种情况:叔节点不是红色,通过旋转解决。LL\RR,父节点染黑作为根节点,祖父节点染成红色,LR\RL新增加的节点染成黑色,祖父节点染成红色。
- 四种情况:叔节点是红色。祖节点染红,当做新节点向上合并,父节点,叔节点染黑。
addAfter代码实现
protected void afterAdd(Node<E> node) { Node<E> parent = node.parent; // 添加的是根节点 或者 上溢到达了根节点 if (parent == null) { black(node); return; } // 如果父节点是黑色,直接返回 if (isBlack(parent)) return; // 叔父节点 Node<E> uncle = parent.sibling(); // 祖父节点 Node<E> grand = red(parent.parent); if (isRed(uncle)) { // 叔父节点是红色【B树节点上溢】 black(parent); black(uncle); // 把祖父节点当做是新添加的节点 afterAdd(grand); return; } // 叔父节点不是红色 if (parent.isLeftChild()) { // L if (node.isLeftChild()) { // LL black(parent); } else { // LR black(node); rotateLeft(parent); } rotateRight(grand); } else { // R if (node.isLeftChild()) { // RL black(node); rotateRight(parent); } else { // RR black(parent); } rotateLeft(grand); } }
测试
- 重写RBtree 的 toString
- 重写RBtree 的 creatorNode
- 每次添加完进行打印
删除
删除 RED 节点
删除 BLACK 节点
删除 拥有一个 RED 节点的 BLACK 节点
删除 - BLACK 叶子节点 - sibling 为BLACK
- 能借的情况:兄弟必须是黑色节点,且黑色兄弟节点有红色子节点
- 总结:
- 如果删除节点的兄弟节点是黑色,能借就通过旋转借。
- 不能借,就拉父节点下来合并
- 如果父节点是红色,则不会下溢
- 如果父节点是黑色(已经有两个黑色子节点,不可能再有红色子节点了),必然下溢。把黑色父节点当做删除的黑色节点继续递归调用删除。
删除 - BLACK 叶子节点 - sibling 为RED
removeAfter 代码实现
- 判断删除的节点是左还是右
- 兄弟节点是红色,更换兄弟
- 兄弟节点是黑色
- 测试
protected void afterRemove(Node<E> node, Node<E> replacement) { // 如果删除的节点是红色 if (isRed(node)) return; // 用以取代node的子节点是红色 if (isRed(replacement)) { black(replacement); return; } Node<E> parent = node.parent; // 删除的是根节点 if (parent == null) return; // 删除的是黑色叶子节点【下溢】 // 判断被删除的node是左还是右 boolean left = parent.left == null || node.isLeftChild(); Node<E> sibling = left ? parent.right : parent.left; if (left) { // 被删除的节点在左边,兄弟节点在右边 if (isRed(sibling)) { // 兄弟节点是红色 black(sibling); red(parent); rotateLeft(parent); // 更换兄弟 sibling = parent.right; } // 兄弟节点必然是黑色 if (isBlack(sibling.left) && isBlack(sibling.right)) { // 兄弟节点没有1个红色子节点,父节点要向下跟兄弟节点合并 boolean parentBlack = isBlack(parent); black(parent); red(sibling); if (parentBlack) { afterRemove(parent, null); } } else { // 兄弟节点至少有1个红色子节点,向兄弟节点借元素 // 兄弟节点的左边是黑色,兄弟要先旋转 if (isBlack(sibling.right)) { rotateRight(sibling); sibling = parent.right; } color(sibling, colorOf(parent)); black(sibling.right); black(parent); rotateLeft(parent); } } else { // 被删除的节点在右边,兄弟节点在左边 if (isRed(sibling)) { // 兄弟节点是红色 black(sibling); red(parent); rotateRight(parent); // 更换兄弟 sibling = parent.left; } // 兄弟节点必然是黑色 if (isBlack(sibling.left) && isBlack(sibling.right)) { // 兄弟节点没有1个红色子节点,父节点要向下跟兄弟节点合并 boolean parentBlack = isBlack(parent); black(parent); red(sibling); if (parentBlack) { afterRemove(parent, null); } } else { // 兄弟节点至少有1个红色子节点,向兄弟节点借元素 // 兄弟节点的左边是黑色,兄弟要先旋转 if (isBlack(sibling.left)) { rotateLeft(sibling); sibling = parent.left; } color(sibling, colorOf(parent)); black(sibling.left); black(parent); rotateRight(parent); } } }
去掉replacement参数
- 在BST.java中,删除replacement参数
private void remove(Node<E> node) { if (node == null) return; size--; if (node.hasTwoChildren()) { // 度为2的节点 // 找到后继节点 Node<E> s = successor(node); // 用后继节点的值覆盖度为2的节点的值 node.element = s.element; // 删除后继节点 node = s; } // 删除node节点(node的度必然是1或者0) Node<E> replacement = node.left != null ? node.left : node.right; if (replacement != null) { // node是度为1的节点 // 更改parent replacement.parent = node.parent; // 更改parent的left、right的指向 if (node.parent == null) { // node是度为1的节点并且是根节点 root = replacement; } else if (node == node.parent.left) { node.parent.left = replacement; } else { // node == node.parent.right node.parent.right = replacement; } // 删除节点之后的处理 afterRemove(replacement); } else if (node.parent == null) { // node是叶子节点并且是根节点 root = null; // 删除节点之后的处理 afterRemove(node); } else { // node是叶子节点,但不是根节点 if (node == node.parent.left) { node.parent.left = null; } else { // node == node.parent.right node.parent.right = null; } // 删除节点之后的处理 afterRemove(node); } }
- RBTree.java
protected void afterRemove(Node<E> node) { // 如果删除的节点是红色 // 或者 用以取代删除节点的子节点是红色 if (isRed(node)) { black(node); return; } Node<E> parent = node.parent; // 删除的是根节点 if (parent == null) return; // 删除的是黑色叶子节点【下溢】 // 判断被删除的node是左还是右 boolean left = parent.left == null || node.isLeftChild(); Node<E> sibling = left ? parent.right : parent.left; if (left) { // 被删除的节点在左边,兄弟节点在右边 if (isRed(sibling)) { // 兄弟节点是红色 black(sibling); red(parent); rotateLeft(parent); // 更换兄弟 sibling = parent.right; } // 兄弟节点必然是黑色 if (isBlack(sibling.left) && isBlack(sibling.right)) { // 兄弟节点没有1个红色子节点,父节点要向下跟兄弟节点合并 boolean parentBlack = isBlack(parent); black(parent); red(sibling); if (parentBlack) { afterRemove(parent); } } else { // 兄弟节点至少有1个红色子节点,向兄弟节点借元素 // 兄弟节点的左边是黑色,兄弟要先旋转 if (isBlack(sibling.right)) { rotateRight(sibling); sibling = parent.right; } color(sibling, colorOf(parent)); black(sibling.right); black(parent); rotateLeft(parent); } } else { // 被删除的节点在右边,兄弟节点在左边 if (isRed(sibling)) { // 兄弟节点是红色 black(sibling); red(parent); rotateRight(parent); // 更换兄弟 sibling = parent.left; } // 兄弟节点必然是黑色 if (isBlack(sibling.left) && isBlack(sibling.right)) { // 兄弟节点没有1个红色子节点,父节点要向下跟兄弟节点合并 boolean parentBlack = isBlack(parent); black(parent); red(sibling); if (parentBlack) { afterRemove(parent); } } else { // 兄弟节点至少有1个红色子节点,向兄弟节点借元素 // 兄弟节点的左边是黑色,兄弟要先旋转 if (isBlack(sibling.left)) { rotateLeft(sibling); sibling = parent.left; } color(sibling, colorOf(parent)); black(sibling.left); black(parent); rotateRight(parent); } } }
红黑树的平衡
红黑树的平均时间复杂度
AVL树 VS 红黑树
BST VS AVL Tree VS RBTree