脑中要构想出连贯的动画,一切都是以这个动画为基础的;然后给别人讲明白
2-3树,有助于理解红黑树
-
理解:
每个节点都可以存放一个元素或者两个元素
存放一个元素的节点称为2-节点、存放两个元素的节点叫做3-节点
每个节点有2个或者3个子节点的树称为2-3树,2-3树满足二叉搜索树的基本性质
2-3树是一个 绝对平衡 的树(平衡因子等于0)
添加节点的时候,是 不可以向null节点插入数据
-
插入节点的不同情景
如果插入2-节点;——》2节点变3节点
如果插入3-节点;——》先形成一个4节点,然后把4节点进行分裂
等等…… -
2-树和红黑树的等价性
2-3树里面的2节点上面的元素相当于黑色节点;如果是3节点,我们这里研究的是左倾红黑树,讲3节点左面的元素作为红色,右面的作为黑色。
-
对比2-3树,引出红黑树的相关特点:
关键点:
以左倾红黑树为基础(红色节点向左倾斜);
根节点必须是黑色;
每个叶子节点必须是黑的;
新插入的节点必须是红的 -
红黑树添加元素的三种情况:
- 新插入的节点在父节点的右边——》左旋+颜色交换
- 3个节点,父节点是黑色,两个子节点是红色的(此时为4节点,不满足2-3树的特征)——》颜色翻转,父节点为红,子节点为黑
- node.color=BLACK,node.left.color=RED,node.left.left.color=RED(此时不满足2-3树特征)——》右旋+颜色翻转
- node.color=BLACK,node.left.color=RED,node.left.right.color=RED——》L+R+CP
- 手撕红黑树
/**
* 老师的代码
* @param <E>
*/
public class RBT<E extends Comparable<E>> {
private static final boolean RED = true;
private static final boolean BLACK = false;
public Node root;
private int size;
public int size() {
return size;
}
//添加节点,递归
public void addEle(E e) {
root = addEle(root, e);
root.color = BLACK;
}
/**
* 将元素E添加到以node为根的那个树上并且将新树的根返回
*
* @param node
* @param e
* @return
*/
private Node addEle(Node node, E e) {
if (node == null) {
size++;
return new Node(e);
}
if (e.compareTo(node.e) < 0) {
node.left = addEle(node.left, e);
} else {
node.right = addEle(node.right, e);
}
//左旋的操作
if (isRed(node.right) && !isRed(node.left)) {
node = leftRotate(node);
}
//右旋
if (isRed(node.left) && isRed(node.left.left)) {
node = rightRotate(node);
}
//颜色翻转
if (isRed(node.right) && isRed(node.left)) {
flipColors(node);
}
return node;
}
// node x
// / \ 右旋转 / \
// x T2 -------> y node
// / \ / \
// y T1 T1 T2
private Node rightRotate(Node node) {
Node x = node.left;
node.left = x.right;
x.right = node;
x.color = node.color;
node.color = RED;
return x;
}
private void flipColors(Node node) {
node.color = RED;
node.left.color = BLACK;
node.right.color = BLACK;
}
// node x
// / \ 左旋转 / \
// T1 x ---------> node T3
// / \ / \
// T2 T3 T1 T2
private Node leftRotate(Node node) {
Node x = node.right;
node.right = x.left;
x.left = node;
x.color = node.color;
node.color = RED;
return x;
}
private boolean isRed(Node node) {
if (node == null) return BLACK;
return node.color;
}
private class Node {
public E e;
public Node left;
public Node right;
public boolean color;
public Node(E e) {
this.e = e;
this.left = null;
this.right = null;
this.color = RED;
}
@Override
public String toString() {
return "Node{" +
"e=" + e +
", color=" + color +
'}';
}
}
/**
* 层次遍历树
*/
public void levelOrder() {
if (root == null) {
return;
}
Queue<Node> queue = new LinkedList();
queue.add(root);
while (!queue.isEmpty()) {
Node curNode = queue.remove();
System.out.println(curNode);
if (curNode.left != null) {
queue.add(curNode.left);
}
if (curNode.right != null) {
queue.add(curNode.right);
}
}
}
}
- 三种树的应用场景
对于完全随机的数据,BST不会出现一侧偏斜的情况,极端情况下会退化成链表或者非常不平衡树
对于查询较多的业务场景,AVL是最佳的选择
RBT的综合性能更优
HashMap在Java8后引入了红黑树,TreeMap、TreeSet的底层也是红黑树