白话红黑树系列之一——初识红黑树
2012-05-16 16:50
232 查看
原文地址:http://blog.csdn.net/arge129/article/details/7572687
红黑树是一种二叉查找树,它能保证在最坏的情况下,基本的动态操作的时间为O(lgn)。
1. 红黑树的性质
红黑树是一种二叉查找树,与普通二叉查找树不同之处在于以下几个方面:
a) 红黑树在每一个结点上增加一个域来存储该结点的颜色,颜色可以是红(RED)的或者是黑(BLACK)的。因此树的结点包括5个域:color,key,left,right,和p,而二叉查找树是不包括color域的。(有关二叉查找树结点的定义,请参考之前我写的文章http://blog.csdn.net/arge129/article/details/7559845)。
b) 为了便于处理代码中的边界条件,我们采用一个哨兵来代替二叉查找树中的空指针(或引用)null,这个哨兵nil[T]是一个与树内普通结点有相同域的对象,它的color域是BLACK,而其他的四个域可以被设置为任意值。于是在二叉查找树中,我们本来指向null的地方指向了哨兵对象nil[T],并且,我们nil[T]为外结点,而带关键字的结点为内结点。为了编程方便,我们使用一个对象来代替所有的哨兵。
c) 一棵二叉查找树如果满足下面的红黑性质,则为一棵红黑树:
1) 每个结点是或是红的,或是黑的。
2) 根结点是黑的。
3) 每个叶结点(nil[T])是黑的。
4) 如果一个结点是红的,那么它的两个儿子是黑的。
5) 对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点。
这5条看起来相当的难记,曾在博客上看过一位大哥总结的不错,在这里写一下,向这位牛人致敬。首先,对于第一条来说,这个似乎没什么地方可以违反(当然后面讲到删除的时候会讲),所以直接忽略就行了。对于第二条和第三条,可以总结为黑头黒脚;第四条,那就是“坑爹”。。。第五条,我自己总结的,“一个都不能少”,这是黄宏的一个电影,讲的是他有25个孩子,然后不管怎么数,一个都不能少(挺感人的,可以去看下),所以,对应到性质里,那就是黑结点不管从哪条路径上来数,都是相同的。因此上面的5条,我们就合并为3条:
1) 黑头黒脚。
2)坑爹。
3)一个都不能少。
是不是好记多了呢?其实,红黑树的性质可以看作是红黑树的定义,如果不满足该性质(定义),那就不是红黑树了。那么来看一棵实际的红黑树吧,读者可以从下图中来验证以上的5个性质或者说咱们总结的那3条。
那么红黑树的这些性质,又能使红黑树具有哪些神奇的功能呢,请看下面。
2. 关于红黑树性质的研究
1) 首先,来看下黑高度的定义。红黑树中某一个结点x的黑高度定义为,从x结点出发(不包括该结点)到达一个叶结点的任意一条路径上,黑色结点的个数称为x结点的黑高度。由“一个都不能少”的性质,我们可以知道,黑高度的定义是明确的,因为从该结点出发的所有下降路径都有相同的黑结点个数。红黑树的黑高度定义为其根结点的黑高度。
2) 一棵有n个内结点的红黑树的高度至多为2lg(n+1)。根据“坑爹”这个性质,从根(不包括根结点)到叶结点的任一条简单路径上至少有一半结点是黑色的。两种极端情况为,一种是树中全是黑结点,一种是红黑结点交替出现。假如红结点在某条路径上的个数超过一半,则必然出现两个连续的红结点的情况,而这与”坑爹“性质矛盾,因此不可能出现红结点在某条路径上超过一半的情况。因此红黑树也确保了没有一条路径会比其他路径长出两倍,因而是接近平衡的。具体的证明参考下算法导论吧,这里写一些证明也没多大意义。根据这个性质,我们可以说,基于红黑树的一些操作可以在O(lgn)时间内完成。
3.代码实现(Java)
本篇主要讨论了红黑树的基本性质,因此在此仅给出Java版本的结点定义,关于红黑树的其他操作,后序博客来完成。谢谢关注。(*^__^*)
红黑树是一种二叉查找树,它能保证在最坏的情况下,基本的动态操作的时间为O(lgn)。
1. 红黑树的性质
红黑树是一种二叉查找树,与普通二叉查找树不同之处在于以下几个方面:
a) 红黑树在每一个结点上增加一个域来存储该结点的颜色,颜色可以是红(RED)的或者是黑(BLACK)的。因此树的结点包括5个域:color,key,left,right,和p,而二叉查找树是不包括color域的。(有关二叉查找树结点的定义,请参考之前我写的文章http://blog.csdn.net/arge129/article/details/7559845)。
b) 为了便于处理代码中的边界条件,我们采用一个哨兵来代替二叉查找树中的空指针(或引用)null,这个哨兵nil[T]是一个与树内普通结点有相同域的对象,它的color域是BLACK,而其他的四个域可以被设置为任意值。于是在二叉查找树中,我们本来指向null的地方指向了哨兵对象nil[T],并且,我们nil[T]为外结点,而带关键字的结点为内结点。为了编程方便,我们使用一个对象来代替所有的哨兵。
c) 一棵二叉查找树如果满足下面的红黑性质,则为一棵红黑树:
1) 每个结点是或是红的,或是黑的。
2) 根结点是黑的。
3) 每个叶结点(nil[T])是黑的。
4) 如果一个结点是红的,那么它的两个儿子是黑的。
5) 对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点。
这5条看起来相当的难记,曾在博客上看过一位大哥总结的不错,在这里写一下,向这位牛人致敬。首先,对于第一条来说,这个似乎没什么地方可以违反(当然后面讲到删除的时候会讲),所以直接忽略就行了。对于第二条和第三条,可以总结为黑头黒脚;第四条,那就是“坑爹”。。。第五条,我自己总结的,“一个都不能少”,这是黄宏的一个电影,讲的是他有25个孩子,然后不管怎么数,一个都不能少(挺感人的,可以去看下),所以,对应到性质里,那就是黑结点不管从哪条路径上来数,都是相同的。因此上面的5条,我们就合并为3条:
1) 黑头黒脚。
2)坑爹。
3)一个都不能少。
是不是好记多了呢?其实,红黑树的性质可以看作是红黑树的定义,如果不满足该性质(定义),那就不是红黑树了。那么来看一棵实际的红黑树吧,读者可以从下图中来验证以上的5个性质或者说咱们总结的那3条。
那么红黑树的这些性质,又能使红黑树具有哪些神奇的功能呢,请看下面。
2. 关于红黑树性质的研究
1) 首先,来看下黑高度的定义。红黑树中某一个结点x的黑高度定义为,从x结点出发(不包括该结点)到达一个叶结点的任意一条路径上,黑色结点的个数称为x结点的黑高度。由“一个都不能少”的性质,我们可以知道,黑高度的定义是明确的,因为从该结点出发的所有下降路径都有相同的黑结点个数。红黑树的黑高度定义为其根结点的黑高度。
2) 一棵有n个内结点的红黑树的高度至多为2lg(n+1)。根据“坑爹”这个性质,从根(不包括根结点)到叶结点的任一条简单路径上至少有一半结点是黑色的。两种极端情况为,一种是树中全是黑结点,一种是红黑结点交替出现。假如红结点在某条路径上的个数超过一半,则必然出现两个连续的红结点的情况,而这与”坑爹“性质矛盾,因此不可能出现红结点在某条路径上超过一半的情况。因此红黑树也确保了没有一条路径会比其他路径长出两倍,因而是接近平衡的。具体的证明参考下算法导论吧,这里写一些证明也没多大意义。根据这个性质,我们可以说,基于红黑树的一些操作可以在O(lgn)时间内完成。
3.代码实现(Java)
本篇主要讨论了红黑树的基本性质,因此在此仅给出Java版本的结点定义,关于红黑树的其他操作,后序博客来完成。谢谢关注。(*^__^*)
/** * 红黑树结点定义 * @author Alfred */ public class RBTreeNode { private int key; private NodeColor color; private RBTreeNode parent; private RBTreeNode left; private RBTreeNode right; public RBTreeNode(int key, NodeColor nodeColor){ this.key = key; this.color = nodeColor; } public String toString(){ return ""+key+":"+color+" "; } /** * @return the key */ public int getKey() { return key; } /** * @param key the key to set */ public void setKey(int key) { this.key = key; } /** * @return the color */ public NodeColor getColor() { return color; } /** * @param color the color to set */ public void setColor(NodeColor color) { this.color = color; } /** * @return the parent */ public RBTreeNode getParent() { return parent; } /** * @param parent the parent to set */ public void setParent(RBTreeNode parent) { this.parent = parent; } /** * @return the left */ public RBTreeNode getLeft() { return left; } /** * @param left the left to set */ public void setLeft(RBTreeNode left) { this.left = left; } /** * @return the right */ public RBTreeNode getRight() { return right; } /** * @param right the right to set */ public void setRight(RBTreeNode right) { this.right = right; } }
/** * 结点颜色 * @author Alfred */ public enum NodeColor { RED, BLACK }
相关文章推荐
- 白话红黑树系列之一——初识红黑树
- 白话红黑树系列之二——红黑树的构建
- 白话红黑树系列之二——红黑树的构建
- 白话经典算法系列之三 希尔排序的实现
- 白话经典算法系列之中的一个 冒泡排序的三种实现
- 龙之历程——学习易经系列(2)之初识乾卦
- 红黑树系列之旋转C++实现
- 大白话系列之C#委托与事件讲解(二)
- Android游戏开发引擎libGDX系列教程(一) -- 初识libGDX
- 白话经典算法系列之六 快速排序 快速搞定
- 大白话系列之C#委托与事件讲解
- 程序员面试、算法研究、编程艺术、红黑树、数据挖掘5大经典原创系列集锦与总结
- 大白话Vue源码系列(04):生成render函数
- 程序员面试、算法研究、编程艺术、红黑树、数据挖掘5大系列集锦
- 白话经典算法系列之七 堆与堆排序
- C++程序设计实践学材系列(3)——第1讲 初识C++程序设计
- Mobvista广告接入——最白话,手把手教你做系列。
- 解读ASP.NET 5 & MVC6系列(2):初识项目
- 白话经典算法系列之七 堆与堆排序
- 【白话经典算法系列之十一】一道有趣的GOOGLE面试题 --【解法2】