ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

简单的语言讲完红黑树要点

2021-06-30 16:58:13  阅读:218  来源: 互联网

标签:黑色 插入 要点 红色 红黑树 讲完 节点 性质


目录

为什么需要红黑树

红黑树的特性

如何插入新节点

最简单的情形:插入的是第一个元素

一般的情形:插入的不是根节点

如何用一个红色节点代替叶子节点


为什么需要红黑树

C++的std::map就是通过红黑树实现的。map相对于list的优点是,取出任何一个元素时,map的效率是O(log n),而list要遍历,其效率是低于map的。

map的高效正是得益于红黑树的特性。

来看一个例子。吴军在其著作《浪潮之巅》中,举了一个猜测世界杯冠军的例子:把32强从1-32编号,竞猜者随意从中挑一个号码:“是第X号吗?比冠军的编号高了还是低了?”假如高了,则从0-X之间取中间数,再猜;假如低了,就从X-32之间挑出中间数再猜。如此往复。这是用二分法快速查询的典型例子,其复杂度是O(\log_{2}32)

红黑树的查找与之类似。假如要查找值X,从根节点触发,每到一个节点,判断被查值X与结点之间是大于、等于还是小于。根据判断结果决定下一步的操作:(走入左分支、返回结果、走入右分支)。其效率也是O(\log_{2}n)

红黑树的特性

为了实现高效率,红黑树被约定了如下的特性:

1 根节点是黑色

2 每个节点有两个子节点,但叶子节点(叶子节点是不具备实际意义的节点)没有子节点。且红色节点的子节点只能是黑色

3 红黑树的末梢节点只能是黑色--这些节点通常是不具备实际意义的节点(叶子节点)

4 从一个节点出发,逐层下行,直到任何一个末梢结束,经历的黑色节点个数都相等。

性质2决定了,在任何一条支路上,红色节点的数目不可能超过黑色节点。再考虑到性质4,可以推论最长的支路节点数最多是最短支路节点数的2倍,不会再多了。

这条推论意义重大。既然希望红黑树的查找效率高于链表,树的根节点就应处于所有元素的中间值附近。远远偏离中间值的红黑树将退化为链表:

图片来源 https://www.cnblogs.com/Mufasa/p/11581141.html

红黑树的性质2,4限制了根节点的位置,不会过度偏离中间值。

补充一条,红黑树每个节点的分叉,都是方向一致的:所有左边分叉的值都小于分叉节点,右边分叉的值都大于分叉节点;或者反过来。否则二分查找是不可能的。

如何插入新节点

对红黑树的操作无外乎查询、插入、删除三种。查询在第一部分已经描述了。

最简单的情形:插入的是第一个元素

将节点染成黑色即可。根节点下面增加两个没有实际意义的叶子节点。

图片来源 https://www.youtube.com/watch?v=JwgeECkckRo

一般的情形:插入的不是根节点

图片来源 https://www.youtube.com/watch?v=JwgeECkckRo 

1)跟查询类似,从根节点开始,逐步判断新节点5与各个节点的大小关系:假如小于节点,左转;反之右转。逐层走下去,直到叶子节点。

2)到达叶子节点后,将新节点染色为红色,代替原有的叶子节点,并在其下面增加两个黑色叶子。

3)接下来要顾虑到性质2--红节点的子节点不能是红色。这一步是整个文章的重点。我分出单独一节来讲。

如何用一个红色节点代替叶子节点

1)假如叶子的父节点是黑色。这时不用调整。红黑树所有的性质仍然被满足。

2)假如父节点是红色,此时为了保证性质2,必须调整某些节点的颜色。

这时无外乎3种情况:

情况A 父节点是红色、叔节点是黑色、祖父节点(必然)是黑色(因为父节点已经是红色)。另外,新节点插入在祖节点分支的最外侧(即最左或者最右)。

情况B  父节点是红色、叔节点是黑色、祖父节点(必然)是黑色(因为父节点已经是红色)。另外,新节点插入在祖节点分支的内侧(即父节点在祖节点左边,但子节点在父节点右边;或者父节点在祖节点右边,但子节点在父节点左边)。

情况C 父节点是红色、叔节点是红色、祖父节点(必然)是黑色(因为父节点已经是红色)

2.1)情况A。对付这种情况的套路可以称之为“换位”

2.1.1)将父节点变成黑色,满足性质2.但是性质4被破坏,因为父节点这条支路上的黑节点个数增加了。

2.1.2)换位--把父节点移动到祖结点的位置上,这样父节点这一支黑节点个数减一。把祖结点变成父节点的子节点,这样叔节点这一支的黑节点个数加一。性质4仍不满足.

2.1.3)但是此时父-祖-叔的三个节点全是黑色。可以将祖结点变成红色,既满足了性质4,又不破坏性质2.

2.2)情况B。这时2.1)的套路不好用了,因为祖节点和新节点在父节点的同侧。祖节点和父节点换位之后,父节点变成了祖节点和子节点共同的父亲。这时两个节点不应该出现在同侧。

 但是这个情况可以转化为情况A,故可以视为A的变种:对调子节点和父节点的位置即转化为情况A。

2.3)情况C。对付这种情况的套路可以称之为“分裂”

2.3.1)将祖结点变成红色,叔节点和父节点变为黑色。这样,本来祖节点贡献的一个黑色节点数目,改为由它的两个子节点贡献。故性质4被满足。此外,父节点的变色也使得性质2被满足。

2.3.2)祖结点变为红色后,有可能会影响与曾祖节点的关系。因为以前祖节点是黑色,所以曾祖节点可能是黑色,也可能是红色。如果曾祖是红色,将破坏性质2.但是这时的情况又跟开始一样了:插入一个红节点。所以回到《如何用一个红色节点代替叶子节点》的开头继续操作。

 综合以上,情况A和情况B是好处理的,但是情况C可能要反复调用,最坏的情况,直到根节点。所以最坏情况下,C的开销最大。但是,不难看出,无论是哪种情况,被操作的只有直系的节点(祖父、父亲)以及与直系直接相连的节点。前面说了,红黑树从上到下的层数大约是O(\log_{2}n)量级。故直系这条线上节点数也是这个量级。就算把与直系相邻的节点也算上,再乘以2即可,不会改变数量级的。所以插入操作的复杂度就是O(\log_{2}n)

有人要问了,操作叔节点,会不会引起与叔节点相邻的其它节点破坏红黑树性质。不会的。首先,以上操作,只会把树节点的颜色从红变黑,不会从黑变红,所以性质2被满足。其次,前面分析了,叔节点变成黑色的同时,祖节点和父节点也随之调整,保证性质4不被破坏。

标签:黑色,插入,要点,红色,红黑树,讲完,节点,性质
来源: https://blog.csdn.net/liji_digital/article/details/118358748

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有