红黑树的概念与性质
前置知识
在学习红黑树之前,最好有二叉查找树和AVL树的基础,因为红黑树本质就是一种特殊的二叉查找树,而红黑树的操作中需要用到AVL树中旋转的相关知识。至于二叉查找树和AVL树,可以参考如下两篇博客:
二叉查找树:二叉查找树-CSDN博客
AVL树:AVL树详解_avl tree-CS DN博客
红黑树是一种特殊的二叉查找树,顾名思义,红黑树的一个特性就是每个节点都有一个颜色特征,或为红或为黑。红黑树可以通过一系列的限制规则保证最长路径小于最短路径的两边,也就是说,红黑树的每一条路径长度的范围为[N, 2N],其中N为最短路径长度。
与AVL树不同的是,红黑树并不是严格意义上的平衡二叉树,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优。
红黑树是通过如下5条性质/规则来维护的:
- 每个结点不是红色就是黑色
- 根节点必须是黑色的
- 红色节点的孩子结点只能是黑色的,即路径上不能出现两个连续的红节点
- 从任一节点到其每个叶子的所有路径,要包含相同数目的黑色节点
- 所有的叶子结点都是黑结点
注意,由于翻译的问题,这里的叶子节点实际上指的其实是NIL节点(空节点)。所以,第5条并不算是一条规则,而是一个性质的说明。
那么为什么说这几条性质就保证了最长路径不会大于最短路径的二倍呢?首先,根节点一定是黑的就保证了每条路径的开始都是一个黑色节点。不能出现连续的红节点而且每条路径的黑色结点数量要保持一致,这种的话最短路径的情况就是整条路径都为黑色的情况,而最长路径的情况就是一黑后面紧跟着一红结点。所以我们很容易应证,最长路径不会超过最短路径的二倍。
红黑树的插入
红黑树本质上就是一种特殊的二叉查找树,所以大纲上我们依旧是按照二叉查找树的方式来插入。但是特别的,红黑树还维护了结点的颜色这一特性,所以我们还需要额外维护结点的颜色已经遵循上述的条规则/性质。
首先我们要思考,插入新结点时,是将其初始化为红色还是黑色。我们知道红黑树要控制每条路径的黑色结点相同,所以为了安全起见,一般是插入节点都默认初始化为红色节点。特别的,红黑树还要保证根节点为黑色,所以我们还需要对根节点的情况特殊处理。也就是说,除了根节点的情况外,新插入的结点都是统一初始化为红色,后续就根据具体情况具体调整了。
那么新插入结点默认为红结点,就必然会出现插入之后连续两个红色结点的情况,那么我们可以将插入之后出现连续红色结点的各种情况分为两大类来说:
首先我们规定,cur为当前新插入的节点,p为父节点,g为祖父节点,u为叔叔节点。
- 情况1:cur为红,p为红,u存在且为红,g为黑
这里需要解释一下,cur为新插入节点,如果p是红的,那么根据红黑树的性质,g一定是黑的。这种叔叔节点存在且为红的情况比较好处理,只需要让父亲节点p和叔叔节点u的颜色置黑,g节点置黑即可。
首先,把p和u置黑是因为不能出现连续的红色节点。而把g置红是因为g并不一定就是根节点,所以为了不影响本条路径的黑色节点的数量,是需要将p置红的。那么如果g真的为根节点的话不就又与性质冲突了吗?所以我们需要特殊情况特殊处理,一般来说前面照常操作,最后之间暴力将根节点颜色设置为黑色是一种较为简便好理解的方式。
我们要知道,红黑树节点调整要在保证不改变路径上黑色节点数量的情况下进行调整,所以当叔叔节点存在且为红的时候,我们就可以通过将叔叔节点置黑,使得在保证路径上黑色节点数量不变的前提下,调整两个连续的红色节点。
- 情况2:cur为红,p为红,g为黑,u不存在/u存在且为黑
那么当叔叔节点为黑色呢?这时我们就无法直接通过简单颜色来进行调整了,此时就需要用到AVL树中提到的4个旋转了。根据p和cur的位置,共有四种旋转的情况。不过由于双旋与单旋之后原来g位置的节点可能为cur也可能为p,所以我们这时就不能单纯的将cur置红或置黑。而是将旋转之后原g位置的节点置黑,将其两个子节点置红,叔叔节点一直是黑的或者空,所以不用对其处理。