二叉树的缺陷,退化成链表的时候,几乎成为线性,不平衡,从而诞生了红黑树
红黑树
满足二叉树的基本特性,同时还有下面的特性:
- 节点是红色或是黑色的
- 根节点是黑色的
- 每个叶子的节点都是黑色的(NIL节点)
- 红色节点的两个子节点都是黑色(不能有连续的红色节点)
- 任意节点到叶子节点的所有路径都包含相同数目的黑色节点
从而保证了红黑树的自平衡,红黑树从根到叶子的最长路径不会超过最短路径的两倍。
插入和删除节点的时候,红黑树的规则有可能被打破,如下图:
因此,需要变色和旋转
变色
插入21,两个红色节点不能相连,22变成黑色。
两个黑色节点不能相连,25变成红色。
此时仍然没有结束,因为节点25和节点27又形成了两个连续的红色节点,需要继续把节点27从红色变成黑色:
左旋转:
逆时针旋转红黑树的两个节点,使得父节点被自己的右孩子取代,而自己成为自己的左孩子。说起来很怪异,大家看下图:
右旋转:
顺时针旋转红黑树的两个节点,使得父节点被自己的左孩子取代,而自己成为自己的右孩子。大家看下图:
何时用旋转?
旋转成
由于根节点必须是黑色节点,所以需要变色,变色结果如下:
这样就结束了吗?并没有。因为其中两条路径(17 -> 8 -> 6 -> NIL)的黑色节点个数是4,其他路径的黑色节点个数是3,不符合规则5。
这时候我们需要把节点13看做X,节点8看做Y,像刚才的示意图那样进行右旋转:
最后根据规则来进行变色:
如此一来,我们的红黑树变得重新符合规则。这一个例子的调整过程比较复杂,经历了如下步骤:
变色 -> 左旋转 -> 变色 -> 右旋转 -> 变色
应用
TreeMap和TreeSet都用到了红黑树,Java8中的HashMap也用到红黑树