红黑树的理解说明(插入)
2015-07-28 17:30
211 查看
本博客很多地方都是引自/article/1362943.html,算是对这篇博客的一个补充吧。
首先先来看一下红黑树5个性质
1、根节点是黑色的
2、每个节点不是黑色的就是红色的
3、叶子节点都是黑色的
4、如果一个节点是红色的,那么他的2个儿子都是黑色的
5、每个节点到叶子节点的每一条路径都包含相同数量的黑节
在插入或者删除节点后,为了保持这5个性质不变,需要做2件事就行了:
1、更改一些节点的颜色
2、交换节点的位置(目的也是为了修改颜色,同时可以使某条路径的红或者黑节点的数量发生改变,书上说的左、右旋)
一、插入
1、插入节点的颜色着为红色(为什么不着为黑色?性质5被破坏)
引用一片博客的伪代码(/article/1362943.html)
RB-INSERT-FIXUP(T, z)
while z.p.color == RED
do if z.p == z.p.p.left
then y ← z.p.p.right
if y.color == RED
then z.p.color ← BLACK ▹ Case 1
y.color ← BLACK ▹ Case 1
z.p.p.color ← RED ▹ Case 1
z ← z.p.p ▹ Case 1
else if z == z.p.right
then z ← z.p ▹ Case 2
LEFT-ROTATE(T, z) ▹ Case 2
z.p.color ← BLACK ▹ Case 3
z.p.p.color ← RED ▹ Case 3
RIGHT-ROTATE(T, z.p.p) ▹ Case 3
else (same as then clause with "right" and "left" exchanged)
T.root.color ← BLACK
首先请记住插入节点的颜色为红色
如果插入节点的父节点为黑色是不会改变红黑树性质,只有插入节点的父节点也是红色的情况下才需要做调整来维持树的性质。
case1:插入节点的父节点红色,叔叔节点红色
操作:
z.p.color ← BLACK ▹ Case 1
z.p.p.color ← RED ▹ Case 1
y.color ← BLACK ▹ Case 1
父节点为红色,那么连续2个红色,违反了性质4,必须设置父节点为黑色才不违反性质4,父节点的颜色由红变黑,变化后在这条路径上的黑节点数就多了一个(+1黑),怎么办?继续往上将祖父节点由原来的黑色(一定是黑色)变为红色,变化后在这条路径上的黑节点数就少了一个(-1黑),经过这2次(+1黑和-1黑)变化颜色,这条路径上的黑节点数还是没有变化,这就保持了性质5,祖父节点由黑变为了红,那么通往他叔叔节点的路径上的黑颜色节点数量就少了一个(-1黑),怎么办?将叔叔节点的红色变为黑色(+1黑),也就全部维持了性质5了。
综合上面的说明,个人觉得伪代码应该这么写,会更容易理解(调换case1标注的2、3行)
z.p.color ← BLACK ▹ Case 1
z.p.p.color ← RED ▹ Case 1
y.color ← BLACK
▹ Case 1
经过这些变化后,性质5(黑节点数量)得到了恢复,然后我们再将这个树看成2段,一段是从祖父节点往下的这一段,一段是从祖父节点往上的一段,显然往下的这一段已经全部将红黑性质全部维持了,往上的那一段呢?不一定,原因是由于祖父节点颜色发生了变化(黑->红),那么在什么情况下性质被破坏?情况只有一种,那就是只有在祖父的父节点的颜色也是红色的情况下,树的第4性质被破坏(连续2个红色节点),其他性质都不会变化,所以后边这句:
z ← z.p.p ▹ Case 1
将当前节点设置到祖父节点,重新进入while循环进行处理,每循环一次,将会将z节点以下的这段树恢复红黑性质,如此这样一段一段往上处理,直到while循环退出,树的性质都被恢复。
case2:插入节点的父节点红色,叔叔节点黑色,当前节点为父节点的右孩子
操作:
z ← z.p ▹ Case 2
LEFT-ROTATE(T, z) ▹ Case 2
为什么要以父节点先进行左旋?单独进行着色的改变能维持性质吗,我们来试一下,先看祖孙的颜色,当前节点红色,父节点红色,祖父节点黑色。
1、首先必须将父节点涂成黑色,那么路径上就多了一个黑色(+1黑)
2、必须将祖父节点涂成红色(-1黑)才能维持性质5
3、祖父涂成红色,那么通往当前节点的叔叔节点的那条路径上就会少了一个黑色,违反了性质5
4、能继续往祖父的父节点一直往上修改颜色来达到性质5的维持吗?答案是不行的,仔细想想,在修改祖父节点颜色后,通往自己的这一条路径上黑颜色节点数量维持了,而通往叔叔那条路径上的黑节点数少了一个,为了维持叔叔路径上的黑节点数量,必须要增加祖宗节点上的黑节点数量,这又导致了通往自己的路径上的黑节点数量不能维持(+1黑)
5、第4步走不通,走另外一种方法,改为右旋祖父节点,这时候可以修复性质5,但是右旋后,右边子树的性质4被破坏(可以自己画图旋转下,祖父节点红,祖父的左子(即当前节点)也为红)
所以我们要用另外一个办法,就是先旋转,先左旋后进入case3,在case3里再做上面这些操作,就能神奇的同时恢复性质4和性质5(精妙!)。
case3:插入节点的父节点是红色,叔叔节点黑色,当前节点为父节点的左孩子
操作:
z.p.color ← BLACK ▹ Case 3
z.p.p.color ← RED ▹ Case 3
RIGHT-ROTATE(T, z.p.p) ▹ Case 3
case2转化到case3,一次搞定修复性质。
首先先来看一下红黑树5个性质
1、根节点是黑色的
2、每个节点不是黑色的就是红色的
3、叶子节点都是黑色的
4、如果一个节点是红色的,那么他的2个儿子都是黑色的
5、每个节点到叶子节点的每一条路径都包含相同数量的黑节
在插入或者删除节点后,为了保持这5个性质不变,需要做2件事就行了:
1、更改一些节点的颜色
2、交换节点的位置(目的也是为了修改颜色,同时可以使某条路径的红或者黑节点的数量发生改变,书上说的左、右旋)
一、插入
1、插入节点的颜色着为红色(为什么不着为黑色?性质5被破坏)
引用一片博客的伪代码(/article/1362943.html)
RB-INSERT-FIXUP(T, z)
while z.p.color == RED
do if z.p == z.p.p.left
then y ← z.p.p.right
if y.color == RED
then z.p.color ← BLACK ▹ Case 1
y.color ← BLACK ▹ Case 1
z.p.p.color ← RED ▹ Case 1
z ← z.p.p ▹ Case 1
else if z == z.p.right
then z ← z.p ▹ Case 2
LEFT-ROTATE(T, z) ▹ Case 2
z.p.color ← BLACK ▹ Case 3
z.p.p.color ← RED ▹ Case 3
RIGHT-ROTATE(T, z.p.p) ▹ Case 3
else (same as then clause with "right" and "left" exchanged)
T.root.color ← BLACK
首先请记住插入节点的颜色为红色
如果插入节点的父节点为黑色是不会改变红黑树性质,只有插入节点的父节点也是红色的情况下才需要做调整来维持树的性质。
case1:插入节点的父节点红色,叔叔节点红色
操作:
z.p.color ← BLACK ▹ Case 1
z.p.p.color ← RED ▹ Case 1
y.color ← BLACK ▹ Case 1
父节点为红色,那么连续2个红色,违反了性质4,必须设置父节点为黑色才不违反性质4,父节点的颜色由红变黑,变化后在这条路径上的黑节点数就多了一个(+1黑),怎么办?继续往上将祖父节点由原来的黑色(一定是黑色)变为红色,变化后在这条路径上的黑节点数就少了一个(-1黑),经过这2次(+1黑和-1黑)变化颜色,这条路径上的黑节点数还是没有变化,这就保持了性质5,祖父节点由黑变为了红,那么通往他叔叔节点的路径上的黑颜色节点数量就少了一个(-1黑),怎么办?将叔叔节点的红色变为黑色(+1黑),也就全部维持了性质5了。
综合上面的说明,个人觉得伪代码应该这么写,会更容易理解(调换case1标注的2、3行)
z.p.color ← BLACK ▹ Case 1
z.p.p.color ← RED ▹ Case 1
y.color ← BLACK
▹ Case 1
经过这些变化后,性质5(黑节点数量)得到了恢复,然后我们再将这个树看成2段,一段是从祖父节点往下的这一段,一段是从祖父节点往上的一段,显然往下的这一段已经全部将红黑性质全部维持了,往上的那一段呢?不一定,原因是由于祖父节点颜色发生了变化(黑->红),那么在什么情况下性质被破坏?情况只有一种,那就是只有在祖父的父节点的颜色也是红色的情况下,树的第4性质被破坏(连续2个红色节点),其他性质都不会变化,所以后边这句:
z ← z.p.p ▹ Case 1
将当前节点设置到祖父节点,重新进入while循环进行处理,每循环一次,将会将z节点以下的这段树恢复红黑性质,如此这样一段一段往上处理,直到while循环退出,树的性质都被恢复。
case2:插入节点的父节点红色,叔叔节点黑色,当前节点为父节点的右孩子
操作:
z ← z.p ▹ Case 2
LEFT-ROTATE(T, z) ▹ Case 2
为什么要以父节点先进行左旋?单独进行着色的改变能维持性质吗,我们来试一下,先看祖孙的颜色,当前节点红色,父节点红色,祖父节点黑色。
1、首先必须将父节点涂成黑色,那么路径上就多了一个黑色(+1黑)
2、必须将祖父节点涂成红色(-1黑)才能维持性质5
3、祖父涂成红色,那么通往当前节点的叔叔节点的那条路径上就会少了一个黑色,违反了性质5
4、能继续往祖父的父节点一直往上修改颜色来达到性质5的维持吗?答案是不行的,仔细想想,在修改祖父节点颜色后,通往自己的这一条路径上黑颜色节点数量维持了,而通往叔叔那条路径上的黑节点数少了一个,为了维持叔叔路径上的黑节点数量,必须要增加祖宗节点上的黑节点数量,这又导致了通往自己的路径上的黑节点数量不能维持(+1黑)
5、第4步走不通,走另外一种方法,改为右旋祖父节点,这时候可以修复性质5,但是右旋后,右边子树的性质4被破坏(可以自己画图旋转下,祖父节点红,祖父的左子(即当前节点)也为红)
所以我们要用另外一个办法,就是先旋转,先左旋后进入case3,在case3里再做上面这些操作,就能神奇的同时恢复性质4和性质5(精妙!)。
case3:插入节点的父节点是红色,叔叔节点黑色,当前节点为父节点的左孩子
操作:
z.p.color ← BLACK ▹ Case 3
z.p.p.color ← RED ▹ Case 3
RIGHT-ROTATE(T, z.p.p) ▹ Case 3
case2转化到case3,一次搞定修复性质。
相关文章推荐
- 一个简单的python连接池,以及DBUtil.PooledDB的使用
- c3p0连接池
- iOS面试题--网络--如何处理多个网络请求的并发的情况
- Windows Message Queue(优先队列)
- Linux命令详解 -- date
- 差分约束系统
- 【Android】保存Fragment切换状态
- JDBC中Statement与PreparedStatement的区别
- java 微信支付所需MD5加密类
- [译]Python编写虚拟解释器
- map容器按value值排序
- Populating Next Right Pointers in Each Node
- Android的屏幕切换动画
- 黑马程序员--学习C语言结构体
- 【十】收获
- textview跑马灯和水平滚动条
- POJ2762 Going from u to v or from v to u?(强联通分量)
- AppStore 发布游戏分辨率列表。 From:https://itunesconnect.apple.com
- nyoj 苹果
- 康拓展开,逆康拓模板