定义:红黑树是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的: 它可以在O(logn)时间内做查找,插入和删除,这里的n是树中元素的数目。
红黑树和AVL树一样都对插入时间、删除时间和查找时间提供了最好可能的最坏情况担保。这不只是使它们在时间敏感的应用如实时应用(real time application)中有价值,而且使它们有在提供最坏情况担保的其他数据结构中作为建造板块的价值。此外,红黑树还是2-3-4树的一种等同,它们的思想是一样的,只不过红黑树是2-3-4树用二叉树的形式表示的。
AVL树:平衡二叉树,它追求的极致的平衡(左右子树高度差不超过1),是一种理想状态,避免不让二叉搜索树退化成链表,又能保证其性能(AVL树新增和旋转要耗费大量的性能),所以才有了一种折中的方案:红黑树,红黑树追求的是黑色平衡,每个节点到叶子节点,经过的黑色节点数量是一样的,这样可以减少旋转的次数
1 预备知识
1.1 二叉查找树
二叉查找树定义:又称为是二叉排序树(Binary Sort Tree)或二叉搜索树。 二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
1) 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
2) 若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
3) 左、右子树也分别为二叉排序树;
常用操作
查找前驱节点:小于当前节点的最大值
查找后继节点:大于当前节点的最小值
删除:本质上是找前驱节点或后继节点来替代当前值
叶子节点直接删除(没有前驱或后继节点)
只有一个子节点的用子节点替代(本质上就是找前驱或者后继节点)
有两个子节点的,需要找到替代节点(替代节点就是前驱或后继节点,这里就有了两种选择)
BST的删除操作和红黑树一样,只不过红黑树多了变色和旋转的操作
BST存在的问题:二叉查找树的高度决定了二叉查找树的查找效率
1.2 2-3-4树
2-3-4树是四阶的B树(这里不要把B当做Balance理解,B没什么意义),他属于一种多路查找树,红黑树的本质就是2-3-4树,他的结构有以下限制:
1.所有叶子结点都拥有相同的深度
2.节点只能是2节点、3节点、4节点之一
2.1 2-节点:包含1个元素的节点,有2个子节点
2.2 3-节点:包含2个元素的节点,有3个子节点
2.3 4-节点:包含3个元素的节点,有4个子节点
2.4 所有节点必须至少包含1个元素
3.元素始终保持排序顺序,整体上保持二叉查找树的性质,即父节点大于左子节点,小于右子节点;而且节点有多个元素时,每个元素必须大于它左边的它的左子树中的元素
2-3-4树对应红黑树
一个2-3-4树对应多个红黑树,一个红黑树只能对应一个2-3-4树
2 红黑树
2.1 红黑树的性质
红黑树是每个节点都带有颜色属性的二叉查找树,颜色为红色或黑色。在二叉查找树强制的一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
性质1. 每个节点不是红色就是黑色。
性质2. 根节点root是黑色。
性质3. 所有叶子都是黑色(叶子是NIL节点(NULL),这类节点不可以忽视,否者代码看不懂)。
性质4. 每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
性质5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点(黑色平衡)。
每个红色节点必须有两个黑色的子节点
2.2 红黑树的操作
1.左旋
/**
* 左旋
* parent parent
* / /
* p pr
* /\ /\
* pl pr ----> p rr
* /\ /\
* rl rr pl rl
* @param p 旋转的支点
*/
private void leftRotate(RBNode<K, V> p) {
if (p == null) return;
RBNode<K, V> parent = p.parent; // 父节点
RBNode<K, V> pr;
if ((pr = p.right) == null) return;
RBNode<K, V> rl = pr.left;
pr.left = p;
p.parent = pr;
p.right = rl;
if (rl != null) {
rl.parent = p;
}
pr.parent = parent;
if (parent != null) {
if (parent.left == p) {
parent.left = pr;
} else parent.right = pr;
} else {
root = pr;
}
}
2 右旋
/**
* 右旋
* parent parent
* / /
* p pl
* /\ /\
* pl pr ----> ll p
* /\ /\
* ll lr lr pr
* @param p 旋转的支点
*/
private void rightRotate(RBNode<K, V> p) {
if (p == null) return;
RBNode<K, V> parent = p.parent; // 父节点
RBNode<K, V> pl;
if ((pl = p.left) == null) return;
RBNode<K, V> lr = pl.right;
pl.right = p;
p.parent = pl;
p.left = lr;
if (lr != null) {
lr.parent = p;
}
pl.parent = parent;
if (parent != null) {
if (parent.left == p) {
parent.left = pl;
} else {
parent.right = pl;
}
} else {
root = pl;
}
}
新增
2.2 红黑树的三种变换规则
旋转和颜色变化规则:所有插入的点默认为红色(如果插入的点默认是黑色,那么即使所有的点都是黑色,也是满足红黑树的特性,这颗红黑树就无法构建了)
2.2.1 改变颜色:当前节点的父亲是红色,且它的祖父节点的另一个子节点(叔叔节点)也是红色
2.1.1 把父节点设为黑色
2.1.2 把叔叔节点也设为黑色
2.1.3 把祖父节点设为红色
2.1.4 把指针定义到祖父节点
2.2.2 左旋:当前父节点是红色,叔叔是黑色的时候,且目前的节点是右子树,以父节点左旋
2.2.3 右旋 :当前父节点是红色,叔叔是黑色的时候,且当前的节点是左子树
2.3.1 把父节点变为黑色
2.3.2 把祖父节点变为红色
2.3.3 以祖父节点旋转
写不下去了,未完待续