红黑树
红黑树的五大特性:
- 根节点是【黑色】
- 【红色】节点的子节点一定都是【黑色】,但是黑色节点的子节点可以是红色和黑色
- 每个
叶子节点
都是黑色的空节点(NIL
),因为叶子节点不存储数据 - 任意一个节点到叶子节点的路径上所包含的【黑色】节点的数量是相同的
这个也称之为【黑色完美平衡】 - 新插入的节点必须是【红色】
为什么?如果新插入的节点是【黑色】,那不管是在插入到那里,一定会破坏黑色完美平衡的
根据以上性质可以推断出:所以红黑树左右子树高度差最大为2倍
——左边n个黑色节点,右边n个黑色节点,然后两个黑色节点中间插入一个红色节点。所以右边n黑色+n红色
为什么红黑树被广泛应用?
二叉搜索树bst极端情况下时间复杂度退化为O(n)
平衡二叉树AVL 虽然效率非常高,但是每次插入、删除都要做调整,所以对于有频繁的插入、删除操作的数据集合,使用 AVL 树的代价就有点高了。
红黑树即不像二叉查找树那样存在复杂度退化的问题,
也不会像完成平衡二叉树那样为了保证完全的平衡而过多的旋转调整。
算是在查找和增删之间取了很好的平衡。
红黑树的高度近似 log2n,增删改查操作的时间复杂度都是 O(logn)
应用场景:
-
java的hashmap、 TreeMap 、TreeSet 底层用到红黑树
-
c++ stl中的map、set底层就是红黑树
-
epoll在内核中的实现,用红黑树管理
文件描述符fd
插入过程的旋转变色
1、当前节点为空,直接插入即可
2、插入的节点已经存在,直接替换即可
3、插入节点的父节点为【黑色节点】,找到父节点,直接插入即可。因为这个树本来就是黑色完美平衡了,再新插入一个新的红色节点,并不会破坏树的平衡以及红黑树的特性。
4、插入节点的父节点为红色
现在,插入的节点的父节点为红色节点,而红色节点一定不可能为根节点,所以可以推断出新插入节点的父节点一定还有父节点
那现在很显然违反了上面的特性 3( 每个【红色】节点的两个子节点一定都是【黑色】),而现在是两个红色节点相连了,这个该怎么处理?此时又继续拆分不同的情况了
第 4-1 种情况:插入节点的叔叔节点存在,且为红色
这个时候你看的树大致是这样子的结构
解决办法:① 将 P 和 U 变成黑色;② 将 PP 变成 红色
如下图:
第 4-2 种情况:叔叔节点不存在,且插入节点的【父节点】是插入节点爷爷节点的【左子节点】
- 第 4-2-1 种情况:
插入节点为其父节点的左子节点,也即 LL 双红色的情况(第一个 L 表示插入节点的父节点,第二个 L 表示插入节点)
这种情况的完整的变换流程如下:
-
第 4-2-2 种情况:
插入节点为其父节点的右子节点,也即 LR 双红色的情况
其完成的变换流程如下:
第 4-3 种情况:叔叔节点不存在,且插入节点的【父节点】是插入节点爷爷节点的【右子节点】
这种情况依旧是继续区分插入节点是其父节点的左子节点还是右子节点 -
第 4-3-1 种情况:
插入节点为其父节点的右子节点,也即 RR 双红色的情况(第一个 R 是插入节点的父节点,第二个 R 是插入节点)
其整个转换流程是这样的
-
第 4-3-2 种情况:
插入节点为其父节点的左子节点,也即 RL 双红色的情况
其完整的转换流程是这样子的: