因为一棵由n个结点随机构造的二叉查找树的高度为lgn,所以顺理成章,二叉查找树的一般操作的执行时间为O(lgn)。但二叉查找树若退化成了一棵具有n个结点的线性链后,则这些操作最坏情况运行时间为O(n)。
于是出现了平衡二叉树,可以看我之前写的博客:
平衡二叉树保证了在最差的情况下,二叉树依然能够保持绝对的平衡,即左右两个子树的高度差的绝对值不超过1。但是这又会带来一个问题,那就是平衡二叉树的定义过于严格,导致每次插入或者删除一个元素之后,都要去维护二叉树整体的平衡,这样产生额外的代价又太大了。二叉搜索树可能退化成链表,而平衡二叉树维护平衡的代价开销又太大了,那怎么办呢?这就要谈到“中庸之道”的智慧了。说白了就是把平衡的定义适当放宽,不那么严格,这样二叉树既不会退化成链表,维护平衡的开销也可以接受。没错,这就是我们要谈的红黑树了。
红黑树是一种含有红黑结点并能自平衡的二叉查找树。它必须除了满足二叉搜索树的性质外,还要满足下面的性质:
每个节点要么是黑色,要么是红色。根节点是黑色。每个叶子节点是黑色。每个红色结点的两个子结点一定都是黑色。任意一结点到每个叶子结点的路径都包含数量相同的黑结点。正是红黑树的这5条性质,使一棵n个结点的红黑树始终保持了logn的高度,从而也就解释了上面所说的“红黑树的查找、插入、删除的时间复杂度最坏为O(log n)”这一结论成立的原因。
1、按照搜索树的特征进行插入
2、插入的时候有意识的碰瓷是红色不能相邻这个特征(即插入的为 红色节点)
3、随着插入的完成
插入的节点是根节点,把颜色改为黑色插入的节点的父节点是黑节点,插入完成插入接节点的父节点为红色,破坏了红色节点不能相邻的原则,需要修复4、插入完成,结果也是一颗红黑树
已知条件:新插入节点(cur)是红色。cur 的父节点(parent)是红色
推导出结论:cur 的祖父节点-- 父亲的父亲(grandpa)一定是黑色
因为,如果grandpa 为红色,则parent 和 grandpa 都是红色,已经不再是红黑树了。
1、uncle是存在的,且颜色为红色,即为下图: grandpa + parent/uncle 代表的是两条路径,在这两条路径上,各自提供了1个黑色
修复规则:任然保证这两条路径上只有一个黑色
即:grandpa:黑 -> 红
parent/uncle:红 -> 黑
然后再考虑 grandpa 和它的父节点问题
cur = grandpa
parent = cur.parent
grandpa = parent.parent
按照同样的规则进行检查,直到 根 or 满足条件
修改后的样子:
情况2:uncle 不存在或者 uncle 存在,颜色为黑色
修复方式:
对 parent 进行右旋
parent:红 -> 黑
grandpa:黑 -> 红 所有路径只有一个黑色
四条路径,黑色颜色不一样多,所以 parent 之前一定右子树,并且两边子树至少有一层黑色
修复后:
修复方式:
对 parent 进行左旋
parent:红 -> 黑
grandpa:黑 -> 红
修复方法:
对 parent 进行 右旋
原来的cur 视为 parent
原来的parent 视为 cur
得到如下: 然后再按照2.1处理
同2.3
最后,把根节点同一改为 黑色
红黑树 和 AVL 树适用范围是重叠的,都是 二叉平衡搜索树,都适合内存级别的数据查找问题
用途:
jdk/c++/linux 中都比较偏向使用红黑树
Windows 偏向使用 AVL树
Java 中使用红黑树的地方
1、TreeMap/TreeSet
2、HashMap如果发生冲突太严重,用红黑树代替链表