个人红黑树笔记
什么是红黑树
红黑树的英文是“Red-Black Tree”,简称 R-B Tree,它是一种不严格的平衡二叉查找树。
红黑树的演变
红黑树自然是一种二叉查找树,这种树结构从根节点开始,左子节点小于它,右子节点大于它。每个节点都符合这个特性,所以易于查找,是一种很好的数据结构。但是它有一个问题,就是容易偏向某一侧,这样就像一个链表结构了,失去了树结构的优点,查找时间会变坏。
所以我们都希望树结构都是矮矮胖胖的,像这样:
而不是这样
在这种需求下,平衡树的概念就应运而生了。
首先是平衡二叉树,这种树结构它不允许左右子树的高度差大于1,
左右子树的高度差大于1,这种要求是非常苛刻的,对于经常要做插入/删除操作的数据来说,并不方便,会大量的消耗资源来做平衡操作。
红黑树就是条件不那么苛刻的平衡树,并且它保持了二叉查找树的性质,查找速度也不比平衡二叉树慢太多
红黑树就是一种平衡树,它可以保证二叉树基本符合矮矮胖胖的结构,但是理解红黑树之前,必须先了解另一种树,叫2-3-4树(4阶B树),红黑树背后的逻辑就是它。
红黑树与2-3-4树
2-3-4树是二叉查找树的变种,树中的2、3和4代表三种节点,以下表示为2-节点3-节点和4-节点。
2-节点:包含一个元素,两条子链接。
一个元素A
左边链接指向小于A的节点
右边链接指向大于A的节点
3-节点,包含2个元素和三条链接:
两个元素A、B
左边的链接指向小于A的节点,中间的链接指向介于A、B值之间的节点,右边的链接指向大于B的节点。
4-节点,包含3个元素和四条链接:
两个元素A、B、C
左边第一个链接指向小于A的节点,第二个链接指向大于A、B值之间节点,第三个链接指向B、C的节点,最后一个链接指向大于C的节点。
2-节点:
3-节点:
4-节点:
在这三种节点的配合下,2-3-4树可以保证在插入值过程中,任意叶子节点到根节点的距离都是相同的。
所以,2-3-4树的设计完全可以保证二叉树保持矮矮胖胖的状态,保持其性能良好。但是,将这种直白的表述写成代码实现起来并不方便,因为要处理的情况太多。这样需要维护三种不同类型的结点,将链接和其他信息从一个结点复制到另一个结点,将结点从一种类型转换为另一种类型等等。
因此,红黑树出现了,红黑树的背后逻辑就是2-3-4树的逻辑,但是由于用红黑作为标记这个小技巧,最后实现的代码量并不大。(但是,要直接理解这些代码是如何工作的以及背后的道理,就比较困难了。所以你一定要理解它的演化过程,才能真正的理解红黑树)
我们来看看红黑树和2-3-4树的关联,首先,最台面上的问题,红和黑的含义。红黑树中,所有的结点都是标准的2-结点,为了体现出3-节点和4-节点,这里将3-结点的两个元素用红色的链接连接起来,即连接了两个2-结点来表示一个3-结点,将4-结点的3个元素用红色的链接连接起来,即连接了三个2-结点来表示一个4-结点。这里红色结点标记就代表指向其的链接是红链接,黑色标记的结点就是普通的结点。所以才会有那样一条定义,叫“从任一结点到其每个叶子的所有简单路径都包含相同数目的黑色结点”,因为红色结点是可以与其父结点合并为一个3-结点或4-结点(父黑结点与子红结点在2-3-4中本就是一个结点),红黑树实现的其实是一个完美的黑色平衡,如果你将红黑树中所有的红色链接放平,那么它所有的叶子结点到根结点的距离都是相同的。所以它并不是一个严格的平衡二叉树,但是它的综合性能已经很优秀了。
这里演示2-3树
红链接放平:
至于为什么说红黑树是 2-3-4树的一种等同呢,这是因为 2-3-4树的每一个结点都对应红黑树的一种结
构,所以每一棵 2-3-4树也都对应一棵红黑树,下图是 2-3-4树不同结点与红黑树子树的对应。
而上文中的 2-3-4树也可以转换成一棵红黑树:
红黑树的性质
性质1:每个节点要么是黑色,要么是红色。
性质2:根节点是黑色。
性质3:每个叶子节点(NIL)是黑色。(叶子是NIL节点,这类节点不可以忽视)
性质4:每个红色节点的两个子节点一定都是黑色。 不能有两个红色节点相连。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
性质5:从任意节点到该结点的所有叶子结点的简单路径都包含相同数目的黑色节点。(黑色平衡)
注意:以下几种单支情况在平衡的红黑树中不可能出现。
因为上述的情况,红黑树处于不平衡状态。(破坏到null,黑色节点数目相同)
所以,平衡状态下红黑树要么单支黑-红,要么有两个子节点。
红黑树的插入
2-3-4树中结点添加需要遵守以下规则:
- 插入都是向最下面一层插入;
- 升元:将插入结点由 2-结点升级成 3-结点,或由 3-结点升级成 4-结点;
- 向 4-结点插入元素后,需要将中间元素提到父结点升元,原结点变成两个 2-结点,再把元素插入
2-结点中,如果父结点也是 4-结点,则递归向上层升元,至到根结点后将树高加1;
而将这些规则对应到红黑树里,就是:
- 新插入的结点颜色为 红色 ,这样才可能不会对红黑树的高度产生影响。
- 3-结点对应红黑树中的 黑+红 子树,插入后将其修复成 红+黑+红 子树(对应 3-结点升元);
- 4-结点对应红黑树中的 红+黑+红 子树,插入后将其修复成 红色祖父+黑色父、叔+红色孩子 子树,然后再把祖父结点当成新插入的红色结点递归向上层修复,直至修复成功或遇到 root 结点;
公式:红黑树+新增一个节点(红色)=对等的2-3-4树+新增一个节点
新增:分情况讨论,主要是要找到插入位置,然后自平衡(左旋或者右旋)且插入节点是红色(如果是黑色的话,那么当前分支上就会多出一个黑色节点出来,从而破坏了黑色平衡),以下分析全部以左子树为例子,右子树的情况则相反。
- 如果插入的是第一个节点(根节点),红色变黑色
- 如果父节点为黑色,则直接插入,不需要变色
- 如果父节点为红色,叔叔节点也是红色(此种情况爷爷节点一定是黑色),则父节点和叔叔节点变黑色,爷爷节点变红色(如果爷爷节点是根节点,则再变成黑色),爷爷节点此时需要递归(把爷爷节点当做新插入的节点再次进行比较)
- 如果父节点是红色,没有叔叔节点或者叔叔节点是黑色(此时只能是NIL节点),则以爷爷节点为支点右旋,旋转之后原来的爷爷节点变红色,原来的父节点变黑色。
右子树的情况和左子树类似,请自行研究,不再赘述。
父结点为黑 叔结点为黑
直接插入
父结点为红 叔结点为红
然后以祖父结点为当前结点,继续判断
例如
双亲结点为红 叔结点为黑
右右
左左
左右
右左
红黑树的删除
2-3-4树的删除可以全部转换为叶子节点的删除,删除原则是先看能不能和下面的叶子节点合并,能合
并的直接合并完后删除,不能合并的就要找个元素替换上去,最终都是要保持平衡。
- 合并==》删除
- 合并==》替换==》删除
- 合并==》无法替换==》再合并==》删除
红色节点一定全部都在多元素节点中
红黑树的删除要比插入要复杂一些,我们还是类比 2-3-4树来讲:
- 查找最近的叶子结点中的元素替代被删除元素,删除替代元素后,从替代元素所处叶子结点开始处理;
- 降元:4-结点变 3-结点,3-结点变 2-结点;
- 2-结点中只有一个元素,所以借兄弟结点中的元素来补充删除后的造成的空结点;
- 当兄弟结点中也没有多个元素可以补充时,尝试将父结点降元,失败时向上递归,至到子树降元成功或到 root 结点树高减1;
将这些规则对应到红黑树中即:
- 查找离当前结点最近的叶子结点作为 替代结点 (左子树的最右结点或右子树的最左结点都能保证替换后保证二叉查找树的结点的排序性质,叶子结点的替代结点是自身)<找前驱或后继>替换掉被删除结点,从替代的叶子结点向上递归修复;
- 替代结点颜色为红色(对应 2-3-4树中 4-结点或 3-结点)时删除子结点直接成功;
- 替代结点为黑色(对应 2-3-4树中 2-结点)时,意味着替代结点所在的子树会降一层,需要依次检验以下三项,以恢复子树高度:
-
兄弟结点的子结点中有红色结点(兄弟结点对应 3-结点或 4-结点)能够“借用”,旋转过来后修正颜色;
-
父结点是红色结点(父结点对应 3-结点或 4-结点,可以降元)时,将父结点变黑色,自身和兄弟结点变红色后删除;
-
父结点和兄弟结点都是黑色时,将子树降一层后把 父结点当作替代结点 递归向上处理。
如上图,删除的要点是 找到替代结点 ,如果替代结点是黑色,递归向上依次判断侄子结点、父结点是否
可以补充被删除的黑色,整体思想就是将删除一个黑色结点造成的影响局限在子树内处理。
注:倒数第二张图是错误的
-
删除(重点):
通俗点讲就是三句话:自己能搞定的自己搞定;搞不定的找兄弟和父亲帮忙;父亲和兄弟都帮不了那有
福同享,有难同当(父亲和兄弟自损)<具体可以看看2-3-4树的删除机制>
- 自己能搞定的自己搞定
- 如果删除的节点对应于2-3-4树的3节点或者4节点,则直接删除,不用跟兄弟和父亲借
- 如果删除的是红色节点,则直接删;如果删除的是黑色节点,则红色节点上来替代,变黑即可
- 搞不定的找兄弟和父亲帮忙
- 前提是找到“真正“的兄弟节点<兄弟结点为红色,肯定不是"真正的兄弟结点">
- 兄弟节点有的借(此时兄弟节点一定是黑色,如果是红色那说明这个节点不是真正的兄弟节点,需要回到上一步找真正的兄弟节点)
- 兄弟节点有两个子节点的情况(2个子节点肯定是红色,如果是黑色的话相当于此时兄弟节点对应2-3-4树是2节点,不可能有多余的元素可以借),此时需要旋转变色
- 兄弟节点只有一个子节点的情况,此时需要旋转变色
- 兄弟和父亲节点帮不了忙,于是开始递归自损<对应2-3-4中的删除结点、父亲结点、兄弟结点合并>
- 前提是找到“真正”的兄弟节点
- 兄弟节点没有多余的元素可借(此时兄弟节点一定为黑色2节点),此时兄弟节点所在分支也要自损一个黑色节点以此达到黑色平衡,最快的方式就是兄弟节点直接变红(相当于就是减少一个黑色节点),此时一父节点为root的子树又达到了平衡(两边都比之前少一个黑色)。但是以祖父节点为root的树依然是不平衡的,此时需要递归处理(因为祖父结点的另一条边,并没有少黑色结点,因此要指向祖父结点,继续将另一边的祖父结点的兄弟设置为红色,来保持平衡,直到祖父结点为红色,将祖父结点设置为黑色,给两边都补充了一个黑色结点,红黑树又保持平衡)
自己能解决
不能自己解决,但兄弟有多余结点
不能自己解决,兄弟也没有多余结点
再来一个