目录
一:红黑树原理
什么是红黑树:
红黑树是一个二叉查找树,但不是高度平衡的树
- 以前交平衡二叉B树
- 每一个节点可以是红或者是黑的
- 红黑树不是高度平衡的,它的平衡是根据自己的红黑规则进行实现的
红黑规则:
- 每一个节点或是红色的,或者是黑色的;
- 根节点必须是黑色;
- 如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节;点,每个叶节点都是黑色的;
- 如果某一个节点是红色的,那么它的子节点必须是黑色(不能出现红色节点相连的情况);
- 对于每一个节点,从该节点到其所有后代叶子节点的简单路径(不能回头,只能往前)上,均包含相同数目的黑色节点
解释一下第5点:我们以17节点为例,我们到15节点的左子节点Nil和右子节点Nil是包含了两个黑色节点,再从17节点到22节点的两个Nil节点也是包含了两个黑色节点。
每一个红黑树的节点都包含下图这些内容
添加节点:
- 添加的节点的颜色,可以是红色的,也可以是黑色的
- 添加节点时默认节点颜色为红色,效率是最高的,添加三个元素,一共只需要调整一次,所以添加效率最高
添加节点时如何保证红黑规则
我们这有十个节点:默认为红色
1. 我们先添加这个20号,因为根节点必须为黑色所以我们将20号颜色改为黑色
2. 现在我们要添加18,因为18比20小,所以添加到20的左子节点上,在添加23号,同理添加到20的右子节点上
结论1:其父节点为黑色,不需要做任何操作。
3.继续添加22号,放在23号的左子节点上。
这里我们就出现了问题,不能出现两个红色的节点相连,那这里我们该怎么解决呢?
解决问题:其父节点为红色,叔叔节点也是红色,我们该怎么解决。
解决方案:
- 将“父节点23”设置为黑色,将“叔叔节点18”设置为黑色
- 将“祖父节点20”设为“红色”
- 如果祖父节点为根节点,则将根节点在次变成黑色
添加节点总结:
现在我们讨论添加节点时,父节点为红色,叔叔节为黑色的时候,应该做的工作。
现在我们有 15 节点和 14 节点
添加15节点,如下图
此时,我们已经违背了红黑规则,即不能有两个红色节点相连的规则 (15和16节点)
- 将“父节点23”设置为黑色,将“叔叔节点18”设置为黑色
- 将“祖父节点20”设为“红色”
- 如果祖父节点为根节点,则将根节点在次变成黑色
完成第一步:
完成第二步:
难点现在来了,我们添加14节点。
其父节点为红色,叔叔节点是黑色,我们既要按照下面的步骤来进行处理:
- 将“父节点15”设置为黑色
- 将“祖父节点16”设为红色
- 以祖父节点为支点进行旋转
第一步:将“父节点15”设置为黑色
第二步:将“祖父节点16”设为红色
第三步:对蓝色箭头的区域进行右旋:
右旋完成:再次遵守红黑规则
红黑树添加总结(全):
二:Java实现红黑树
红黑树API设计以及代码实现:
package com.wt.redblacktree; public class RedBlackTree<Key extends Comparable<Key>, Value> { //根节点 private Node root; //记录树中元素的个数 private int N; private static final boolean RED = true; private static final boolean BLACK = false; private class Node { //存储键 public Key key; //存储值 private Value value; //记录左子节点 public Node left; //记录右子节点 public Node right; //由其父节点指向它的链接的颜色 public boolean color; public Node(Key key, Value value, Node left, Node right, boolean color) { this.key = key; this.value = value; this.left = left; this.right = right; this.color = color; } } //获取树中元素的个数 public int size() { return N; } /** * 判断当前节点的父指向链接是否为红色 * * @param x * @return */ private boolean isRed(Node x) { //传进来有可能为NULL if (x == null) { return false;//返回false表示空连接为黑色 } else { return x.color == RED; } } /** * 左旋 * * @param h * @return */ private Node rotateLeft(Node h) { //获取h节点的右子节点,表示为x, Node x = h.right; //让x节点的左子节点成为h节点的右子节点 h.right = x.left; //让h节点成为x节点的左子节点 x.left = h; //让x节点的color属性,等于h节点的color属性 h.color = x.color; //让h节点的color属性变为红色 h.color = RED; //返回一个节点 return x; } /** * 右旋 * * @param h * @return */ private Node rotateRight(Node h) { //获取h节点的左子节点,表示为x Node x = h.left; //让x节点的右子节点成为h节点的左子节点 h.left = x.right; //让h节点成为x节点的右子节点 x.right = h; //让x节点的color属性变成h节点一开始的属性 h.color = x.color; // 让h节点的color属性变成红色 h.color = RED; return x; } /** * 颜色翻转 * * @param h */ private void flipColors(Node h) { //当前节点变为红色 h.color = RED; //左子节点和右子节点变为黑色 h.left.color = BLACK; h.right.color = BLACK; } /** * 在树上完成插入操作 * * @param key * @param val */ public void put(Key key, Value val) { root = put(root, key, val); root.color = BLACK;//根节点的颜色总是黑色的 } /** * 在指定数中,完成插入操作,并返回添加元素后新的树,这里才是插入的核心 * * @param h * @param key * @param val * @return */ private Node put(Node h, Key key, Value val) { //判断h是否为空,如果为空则直接返回一个红色的节点即可 if (h == null) { //数量加1 N++; return new Node(key, val, null, null, RED); } //比较h节点的键和key的大小 int cmp = key.compareTo(h.key); if (cmp < 0) { //继续往左 h.left = put(h.left, key, val); } else if (cmp > 0) { //继续往右 h.right = put(h.right, key, val); } else { //等于的话,发生值的替换即可 h.value = val; } //插入已经完成,还要做一件事情 //进行左旋:当 当前节点h的左子节点为黑色,右子节点为红色,需要左旋 if (isRed(h.right) && !isRed(h.left)) { //调用左旋 h = rotateLeft(h); } //进行右旋:当当前节点的h的左子节点和左子节点的左子节点都为红色,需要右旋 if (isRed(h.left) && isRed(h.left.left)) { h = rotateRight(h); } //进行颜色翻转:当前节点的左子节点和右子节点都为红色时,需要颜色翻转 if (isRed(h.left) && isRed(h.right)) { flipColors(h); } return h; } //获取相关的算法 //根据key从树中找出对应的值 public Value get(Key key) { return get(root, key); } //从指定的数x中,查找key对应的值 public Value get(Node x, Key key) { if (x == null) { //就证明没有key对应的值 return null; } //比较x节点的键和key的大小 int cmp = key.compareTo(x.key); if (cmp < 0) { //继续往左查找 return get(x.left, key); } else if (cmp > 0) { //继续往右查找 return get(x.right, key); } else { return x.value; } } }
测试代码:
package com.wt.redblacktree;
public class RedBlackTreeTest {
public static void main(String[] args) {
//创建红黑树
RedBlackTree<String,String> tree = new RedBlackTree<>();
//往树中插入元素
tree.put("1","张三");
tree.put("3","李四");
tree.put("2","王五");
tree.put("4","赵六");
tree.put("6","小七");
tree.put("5","嘿嘿");
//从树中获取元素
String s1 = tree.get("1");
System.out.println(s1);
String s2 = tree.get("2");
System.out.println(s2);
String s3 = tree.get("3");
System.out.println(s3);
String s4 = tree.get("4");
System.out.println(s4);
String s5 = tree.get("5");
System.out.println(s5);
String s6 = tree.get("6");
System.out.println(s6);
}
}
运行结果:
以上就是我对红黑树规则的理解和java代码的实现,希望可以帮助到大家。