1. 前言
红黑树是二叉搜索树的一种, 为什么要设计红黑树呢?主要原因是二叉搜索树上面的操作,比如search、insert、delete的一些操作的,其时间复杂度为O(h)。主要和树的深度有关。因此搜索树的深度较低时,这些集合操作执行地比较快。然而,如果树的高度较高的时候,这些集合操作可能比不上链表的执行得快。那么为了设计一种高效地二叉搜索树,主要的做法就是降低树的深度。红黑树就是为了降低二叉搜索树的深度来设计的,可以保证对红黑树的操作在最坏和最好的情况下基本动态集合操作的时间复杂度为O(logN)。
2. R-B Tree简介
R-B Tree,全称是Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找树。红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black)。对红黑树的操作在最坏的情形下花费O(logN)时间。
红黑树的特性:
- 每个节点或者是黑色,或者是红色。
- 根节点是黑色。
- 每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
- 如果一个节点是红色的,则它的子节点必须是黑色的。
- 从一个节点到null引用的每一条路径必须包含相同数目的黑色节点。
红黑树的着色法则的一个结论,红黑树的高度最多是2log(N+1)
3. R-B Tree应用
红黑树的应用比较广泛,主要是用它来存储有序的数据,它的时间复杂度是O(lgn),效率非常之高。
例如,Java集合中的TreeSet和TreeMap,C++ STL中的set、map,以及Linux虚拟内存的管理,都是通过红黑树去实现的。
4. 红黑树的基本操作:左旋和右旋
红黑树的基本操作是添加、删除。在对红黑树进行添加或删除之后,都会用到旋转方法。为什么呢?道理很简单,添加或删除红黑树中的节点之后,红黑树就发生了变化,可能不满足红黑树的5条性质,也就不再是一颗红黑树了,而是一颗普通的树。而通过旋转,可以使这颗树重新成为红黑树。简单点说,旋转的目的是让树保持红黑树的特性。
旋转包括两种:左旋 和 右旋。下面分别对它们进行介绍。
4.1 左旋
对x进行左旋,意味着"将x变成一个左节点"。
理解左旋之后,看看下面一个更鲜明的例子。你可以先不看右边的结果,自己尝试一下。
4.2 右旋
对Y进行右旋,意味着"将Y变成一个右节点"。
理解右旋之后,看看下面一个更鲜明的例子。你可以先不看右边的结果,自己尝试一下。
4.3 区分左旋和右旋
仔细观察上面"左旋"和"右旋"的示意图。我们能清晰的发现,它们是对称的。无论是左旋还是右旋,被旋转的树,在旋转前是二叉查找树,并且旋转之后仍然是一颗二叉查找树。
左旋示例图(以x为节点进行左旋):
z
x /
/ \ --(左旋)--> x
y z /
y
对x进行左旋,意味着,将“x的右孩子”设为“x的父亲节点”;即,将 x变成了一个左节点(x成了为z的左孩子)!。 因此,左旋中的“左”,意味着“被旋转的节点将变成一个左节点”。
右旋示例图(以x为节点进行右旋):
y
x \
/ \ --(右旋)--> x
y z \
z
对x进行右旋,意味着,将“x的左孩子”设为“x的父亲节点”;即,将 x变成了一个右节点(x成了为y的右孩子)! 因此,右旋中的“右”,意味着“被旋转的节点将变成一个右节点”。
5. 红黑树的基本操作:添加
将一个节点插入到红黑树中,需要执行哪些步骤呢?首先,将红黑树当作一颗二叉查找树,将节点插入;然后,将节点着色为红色;最后,通过旋转和重新着色等方法来修正该树,使之重新成为一颗红黑树。详细描述如下:
- 第一步: 将红黑树当作一颗二叉查找树,将节点插入。
- 第二部:将插入的节点着色为"红色"。
- 第三步:通过一系列的旋转或着色等操作,使之重新成为一颗红黑树。
6. 红黑树基本操作:删除
将红黑树内的某一个节点删除。需要执行的操作依次是:首先,将红黑树当作一颗二叉查找树,将该节点从二叉查找树中删除;然后,通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。详细描述如下:
- 第一步:将红黑树当做是一颗二叉查找树,将节点删除。
- 第二步:通过“旋转和重新着色”等一系列操作来修正该树,使之重新成为一颗红黑树。’,