一.为什么要有红黑树这种数据结构?
我们知道ALV树是一种严格按照定义来实现的平衡二叉查找树,所以它查找的效率非常稳定,为O(log n),由于其严格按照左右子树高度差不大于1的规则,插入和删除操作中需要大量且复杂的操作来保持ALV树的平衡(左旋和右旋),因此ALV树适用于大量查询,少量插入和删除的场景中
那么假设现在假设有这样一种场景:大量查询,大量插入和删除,现在使用ALV树就不太合适了,因为ALV树大量的插入和删除会非常耗时间,那么我们是否可以降低ALV树对平衡性的要求从而达到快速的插入和删除呢?
答案肯定是有的,红黑树这种数据结构就应运而生了(因为ALV树是高度平衡的,所以查找起来肯定比红黑树快,但是红黑树在插入和删除方面的性能就远远不是ALV树所能比的了)
二.红黑树的简介
红黑树是一种特殊的二叉查找树,每个结点都要储存位表示结点的颜色,或红或黑
红黑树的特性:
1)每个结点或红或黑
2)根结点是黑色
3)空叶子结点是黑色
4)如果一个结点是红色,那么它的子节点是黑色
5)从任意一个结点出发到空的叶子结点经过的黑结点个数相同(比如下图中80到任意一个空叶子结点经过的黑结点都是3)
示意图:(红黑树首先是一颗搜索树,所以左子树点小于根结点,右子树大于根节点,等于根结点的按照自己的需要放左边和右边都是可以的)(红黑树不仅是一颗搜索树还是一颗非严格的平衡树)
图1
现在有个问题:红黑树是怎么通过上面5条性质保证任意结点到空的叶子结点的所有路径中,没有一条路径会大于其他路径的两倍的呢?(换句话说就是它是如何确保任何一个结点的左右子树的高度差不会超过二者中较低那个的一倍的呢?)
先结合红黑树的5个性质分析一下红黑树的一些操作,我们可能就明白了!
三.红黑树的基本操作之旋转
红黑树的基本操作是添加和删除,在对红黑树进行添加和删除之后,都会用到旋转方法,为什么呢?道理很简单,因为添加或者删除红黑树中的结点之后,红黑树就发生了变化,可能不满足上面的5条性质了,这个时候就需要通过旋转操作来保证它依旧是一棵红黑树,旋转分为左旋和右旋
(旋转操作仅仅只是用来调节结点的位置的,就是为了满足红黑树的性质5)
1.左旋
图2
左旋是将X的右子树绕X逆时针旋转,使得X的右子树成为X的父亲,同时修改相关结点的引用,旋转之后,要求二叉查找树的属性依然满足
2.右旋
图3
右旋是将X的左子树绕X顺时针旋转,使得X的左子树成为X的父亲,同时注意修改相关结点的引用,旋转之后要求仍然满足搜索树的属性
四.红黑树的基本操作之添加元素
添加操作宏观过程:首先将红黑树当作一颗查找树一样将结点插入,然后将结点着为红色,最后通过旋转和重新着色的方法使之重新成为红黑树
将新加入的结点涂成红色的原因:
1)不违背红黑树的性质5:从任意一个结点出发到空叶子结点,经过的黑色结点个数相同
2)按照红黑树的性质4我们知道红黑树中黑结点的个数至少是红结点个数的两倍,所以新增结点的父亲结点是黑结点的概率比较大,如果新增结点的父节点为黑色,那么此时不需要再去进行任何调整操作,因此效率很高,所以新结点应该涂成红色
少违背一条性质,意味着我们后续的旋转和重新着色操作会简单很多
现在我们来看看新增一个红色的结点会违背红黑树的5条性质中的哪些?
1)每个结点或红或黑
2)根结点是黑色
3)空叶子结点是黑色
4)如果一个结点是红色,那么它的子节点是黑色
5)从任意一个结点出发到空的叶子结点经过的黑结点个数相同
1.显然没有违背
2.根据查找树的特定,插入操作不好改变根结点,所以也没有违背
3.插入的肯定不是空叶子结点,所以也没有违背
4.有可能违背!!!
5.插入结点涂成红色就是为了不违背第5条性质
现在我们来分析一下新增的结点(红色)插入之后可能面临的几种情况,以及他们的处理措施
1.插入的结点为根结点
将新插入的红色结点变成黑色结点,满足根结点为黑色结点的要求!
2.父亲结点为黑色结点
这个时候不需