红黑树
内容参考 《算法导论》
红黑树是一颗二叉搜索树,他的每个节点上增加了一个存储位来表示节点的颜色(RED OR BLACK)。通过对任何一条根从叶子的简单路径上各个节点的颜色进行约束,红黑树确保没有一条路径会比其他路径长出两倍,因而是近似于平衡的。
树上面的每个节点包含5个属性: color 、 key 、 left 、 right 、 p 。如果一个节点没有子节点或者父节点,则该节点相应指针属性的值为 NIL 。我们可以把这些 NIL 视为指向二叉搜索树的叶节点(外部节点) 的指针,而把带关键字的节点视为树的内部节点。
一颗红黑树是满足下面 红黑性质 的二叉搜索树:
- 每个节点是 红色 或者是 黑色 的
- 根节点是黑色的
- 每个叶节点(NIL) 是黑色的
- 如果一个节点是红色的,则他的两个子节点都是黑色的
- 对每个节点,从该节点到其所有的后代叶节点的简单路径上,均包含相同数目的黑色节点。
红黑树示例
为了方便处理红黑树代码中的边界条件,使用一个哨兵赖掉表NIL。对于一颗红黑树T,哨兵T.NIL 是一个与树种普通节点有相同属性的对象。他的color属性为BLACK,其他属性()可以设为任意值。上图中所有指向 NIL 的指针都用指向哨兵 T.NIL 的指针替换。
使用哨兵后,就可以将节点x 的NIL孩子视为一个普通节点,其父节点为x。尽管可以为树内的每一个NIL新增一个不同的哨兵节点,是的每个 NIL 的父节点都有这样的两定义,但是这种做法会浪费空键,取而代之的是:使用一个 T.NIL 代表所有的NIL。
我们通常将注意力放在红黑树的内部节点上,因为他们存储了关键字的值。
从某个节点 x 出发(不含该节点)到达一个节点的任意一条简单路径上的黑色节点个数称为该节点的黑高(Black-height),记为bh(x)。更具性质5,黑稿的概念是明确定义的,因为从该节点出发的所有下降到其叶节点的简单路径的黑节点个数都相同。于是定义红黑树的黑高为其根节点的黑高。
可以看出,每个节点到他任何一个叶子节点经过黑色节点的数目是相同的。
证明:
h = 树的高度。
n = 树的节点数。
根据性质4,从根到叶节点的任何一条简单路径上都至少有一半的节点为黑色。即黑高
推导出:
->
由该结论,动态集合操作 SEARCH 、 MINIMUM 、 MAXIMUM 、 SUCCESSOR 、 PREDECESSOR 可在红黑树上在O(lg n) 时间内执行,因为这些操作在一棵高度为h的二叉搜索树上的运行时间为O(h),而任何包含n个节点的红黑树又都是高度为O(lgn)的二叉搜索树。
红黑树的旋转
右旋转
当然这里不应该旋转,因为一旋转,叶子节点层次差超过了1.这里只是示范旋转
新增节点
在AVL平衡二叉树种,新增节点就要考虑到平衡的要求了【叶子节点的高度差不得超过1】,如果超过了就要进行旋转来维持平衡。而在红黑树中就需要重新染色和平衡了。
再插入的时候会遇到三种情况:
- 当前节点的父节点是红色且祖父节点的另一个子节点(叔叔节点)是红色
- 当前节点的父节点是红色,叔叔节点是黑色,当前节点是其父节点的右孩子
- 当前节点的父节点是红色,叔叔节点是黑色,当前节点是其父节点的左孩子
图示
下面将图解在上面的红黑树中插入4展示三种情况的处理方式
可以看到解决办法
情况一:重新染色祖父节点和父节点,满足性质四(红色节点的两个孩子都是黑色节点)
情况二:左旋转
情况三:右旋转,并重新染色旋转的两个节点
删除节点
删除节点也会遇见四重情况
- 兄弟节点是红色的
- 兄弟节点是黑色的,而且兄弟节点的两个子节点都是黑色的
- 兄弟节点是黑色的,兄弟节点的左孩子是红色的,右孩子是黑色的
- 兄弟节点是黑色的,且兄弟节点的右孩子是红色的
图解示例
解决方案