赞
踩
简称RBTree,也是一种自平衡的二叉搜索树。
大名鼎鼎红黑树。
parent
都是BLACK;红黑树和4阶B树具有等价性:
parent
:父节点;sibling
:兄弟节点;uncle
:叔父节点-parent
的兄弟节点;grand
:祖父节点;这里直接继承BST
代码。
// 节点类 protected static class Node<E>{ // 元素值 E e; // 左子节点 Node<E> left; // 右子节点 Node<E> right; // 父节点 Node<E> parent; public Node(E e, Node<E> parent){ this.e = e; this.parent = parent; } /** * 判断当前节点是否是叶子节点 * @return */ protected boolean isLeaf(){ return left == null && right == null; } /** * 判断当前节点是否存在左右子节点 * @return */ protected boolean hasTwoChildren(){ return left != null && right != null; } /** * 判断是不是父节点的左子树 * @return */ public boolean isLeftChild(){ return parent != null && this == parent.left; } /** * 判断是不是父节点的右子树 * @return */ public boolean isRightChild(){ return parent != null && this == parent.right; } /** * 返回兄弟节点 * @return */ public Node<E> sibling(){ if (isLeftChild()){ return parent.right; } if (isRightChild()){ return parent.left; } return null; } }
node
节点): private static class RBNode<E> extends Node<E>{
boolean color = RED;
public RBNode(E e, Node<E> parent) {
super(e, parent);
}
}
// 节点数量
protected int size;
// 根节点
protected Node<E> root;
private static final boolean RED = false;
private static final boolean BLACK = true;
/** * 染色 */ private Node<E> color(Node<E> node,boolean color){ if (node == null){ return node; }else { ((RBNode<E>)node).color = color; return node; } } /** * 染色成红色 * @param node * @return */ private Node<E> red(Node<E> node){ return color(node, RED); } /** * 染色成黑色 * @param node * @return */ private Node<E> black(Node<E> node){ return color(node, BLACK); } /** * 判断颜色 * @param node * @return */ private boolean colorOf(Node<E> node){ return node == null ? BLACK : ((RBNode<E>)node).color; } /** * 是否为黑色 * @param node * @return */ private boolean isBlack(Node<E> node){ return colorOf(node) == BLACK; } /** * 是否为红色 * @param node * @return */ private boolean isRed(Node<E> node){ return colorOf(node) == RED; }
添加时的元素都是添加到叶子节点。
默认新添加的节点为红色。
parent
染色成BLACK
,将grand
染色成RED
;grand
染成RED;四种情况:LL、RR、LR、RL都采用上溢的方法来恢复平衡,可以参考上篇博客。
这里只展示了LL:
uncle
和parent
都染成BLACK;grand
染色为RED作为新节点向上合并,可能会继续触发上溢,如果持续到根节点,只需将根节点染成BLACK。AVL
和RBTree
都继承自BST
,单因为RBTree
需要复用AVL
的代码,所以这里新建一个平衡二叉搜索树BBST
,将旋转代码放到里面,原先AVL
更新高度的代码放到AVL
中自己去实现。/** * @Description 平衡二叉搜索树 * @date 2022/4/20 15:15 */ public class BBST<E> extends BinarySearchTree<E> { public BBST(){ this(null); } public BBST(Comparator<E> Comparator){ super(Comparator); } /** * 左旋 * @param grand */ protected void rotateLeft(Node<E> grand){ Node<E> parent = grand.right; Node<E> child = parent.left; grand.right = child; parent.left = grand; afterRotate(grand, parent, child); } /** * 右旋 * @param grand */ protected void rotateRight(Node<E> grand){ Node<E> parent = grand.left; Node<E> child = parent.right; grand.left = child; parent.right = grand; afterRotate(grand, parent, child); } /** * 旋转之后 更新 parent 和 height * @param grand * @param parent * @param child */ protected void afterRotate(Node<E> grand,Node<E> parent,Node<E> child){ // 更新 parent // 更新 p 的 parent 使成为当前子树的根节点 parent.parent = grand.parent; if (grand.isLeftChild()){ grand.parent.left = parent; }else if (grand.isRightChild()){ grand.parent.right = parent; }else { // grand 是根节点 root = parent; } // 更新 p.left 的 parent if (child != null){ child.parent = grand; } // 更新 g 的 parent grand.parent = parent; } /** * 旋转 * @param r 子树根节点 * @param a * @param b * @param c * @param d * @param e * @param f * @param g */ protected void rotate( Node<E> r, Node<E> a, Node<E> b, Node<E> c, Node<E> d, Node<E> e, Node<E> f, Node<E> g){ // 让 d 成为这颗子树的根节点 d.parent = r.parent; if (r.isRightChild()){ r.parent.right = d; }else if (r.isLeftChild()){ r.parent.left = d; }else { root = d; } // a b c b.left = a; if (a != null){ a.parent = b; } b.right = c; if (c != null){ c.parent = b; } // e f g f.left = e; if (e != null){ e.parent = f; } f.right = g; if (g != null){ g.parent = f; } // b d f d.left = b; b.parent = d; d.right = f; f.parent = d; } }
B树中,所以真正被删除的元素都是在叶子节点中。红黑树也遵循相同的性质。
修改代码结构: 在原先afterRemove()
方法的基础上新增参数replacement
替代被删除的节点。修改BinarySearchTree.remove()
:传入replacement
参数。
不需要做任何处理。
有两个RED子节点:找它的子节点代替删除;
判定条件:用以替代的子节点是RED。
等同于B树的性质,如果删除节点是发生下溢,优先看兄弟节点是否可以“借”一个节点。这就要求兄弟节点为BLACK,并且至少有一个RED的子节点。
parent
(80)的颜色;左右节点染为BLACK。当兄弟节点没有红色子节点无法借出时,需要让父节点下来合并。
parent
是BLACK,就会导致parent
也下溢(父节点必然没有红色子节点)。parent
节点传入即可。parent
染色成RED
,进行右旋:又回到了兄弟节点是黑色的情况。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; } // 兄弟节点必然是黑色 System.out.println(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); } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。