一.基本概念
首先,红黑树是二叉搜索树,但不一定是平衡树。
红黑树的五条性质:
1.每个结点非红即黑,必定有颜色。
2.根结点是黑的,值为null的节点也是黑的。
3.每个叶子结点都是黑的。
4.如果一个结点是红的,那么它的两个儿子都是黑的。
5.对于任意结点而言,其到叶结点树尾端的每条路径都包含相同数目的黑结点。
其次,红黑树在Java中的应用也有具现。
TreeMap,HashMap(哈希冲突多于8个底层就从链表转为红黑树了)底层都用到了红黑树。
刚接触红黑树的时候我也在想,都有了AVL树这么优秀的平衡树了,红黑树存在的意义是什么呢?
在查阅了相关资料后我发现:
1.删除的时候AVL树为了节点平衡,复原树的时候开销大;
删除节点导致失衡,AVL树需要维护从被删除节点到根节点root这条路径上所有节点的平衡,旋转的量级为O(logN),而红黑树最多只需要旋转3次实现复衡,只需O(1),所以说红黑树删除节点后的复原树的效率更高,开销更小!
2.插入节点导致树失衡的情况,AVL树和红黑树都是最多两次树旋转来实现复原树操作,旋转的量级都是O(1)。
3.由于AVL树高度平衡,因此AVL树的查询效率更高。
4.在百万级的情况下,红黑树并没有AVL树效率高多少。
5.在千万级或上亿级的情况下,红黑树才体现出比AVL树效率高。
二.红黑树的左旋转和右旋转
1.红黑树的左旋转:
先上一下节点类型的定义:
/**
* 节点类型定义
*
* @param <T>
*/
private static class Entry<T extends Comparable<T>> {
T data;
Entry<T> left;
Entry<T> right;
Entry<T> parent;
Color color;
public Entry(T data, Entry<T> left, Entry<T> right, Entry<T> parent, Color color) {
this.data = data;
this.left = left;
this.right = right;
this.parent = parent;
this.color = color;
}
}
/**
* 指向红黑树的根节点
*/
private Entry<T> root;
public RBTree() {
this.root = null;
}
然后图解一下红黑树的左旋转操作:
红黑树左旋转的代码:
/**
* 节点的左旋转操作
*
* @param node
*/
private void leftRotate(Entry<T> node) {
Entry<T> child = node.right;
child.parent = node.parent;
//判断node节点是否为根节点,如果是,那么child将成为根节点
if (null == node.parent) {
this.root = child;
} else {
//判断node节点是它父亲节点的左孩子还是右孩子,再进行移动
if (node.parent.left == node) {
node.parent.left = child;
} else {
node.parent.right = child;
}
}//以上为图中的 1 操作
node.right = child.left;
if (null != child.left) {
child.left.parent = node;
}
//以上为图中的 2 操作
child.left = node;
node.parent = child;
//以上为图中的 3 操作
}
2.红黑树的右旋转
和左旋转道理一样,就是旋转方向不同。
代码:
/**
* 节点的右旋转操作
*
* @param node
*/
private void rightRotate(Entry<T> node) {
Entry<T> child = node.left;
child.parent = node.parent;
if (null == node.parent) {
this.root = child;
} else {
if (node.parent.left == node) {
node.parent.left = child;
} else {
node.parent.right = child;
}
}
node.left = child.right;
if (null != child.right) {
child.right.parent = node;
}
child.right = node;
node.parent = child;
}