`
java-mans
  • 浏览: 11449768 次
文章分类
社区版块
存档分类
最新评论

算法导论 红黑树

 
阅读更多

原文转自:http://www.886s.com/blog/?p=30

组内培训,讲红黑树,找出算法导论,啃了一个周末,其中插入结点很简单,删除结点有点复杂,但跟着算法导论上一步一步来没有什么问题。不想备份blog的图片,所以没有把图片上穿。可直接察看ppt。

红黑树性质

1.每个节点或是红的,或是黑的

2.根节点是黑的

3.每个叶结点(NIL)都是黑的

4.如果一个结点是红的,则它的两个儿子都是黑的

5.对每个结点,从该节点到其子孙结点的所有路径上包含相同数目的黑结点

*从某个结点x出发(不包括该结点)到达一个叶结点的任意一条路径上,黑色结点的个数成为该结点x的黑高度,用bh(x)表示。

*引理:一颗有n个内节点的红黑树的高度之多为2lg(n+1)

插入不变式

a)结点z是红色。

b)如果p[z]是根,则p[z]是黑色。

c)如果有红黑树的性质被破坏,则至多只有一个被破坏,并且不是性质2)就是性质4)。如果违反性质2),则发生的原因是z是根而且是红的。如果违反性质4),则原因是z和p[z]都是红色的,则p[p[z]]必然是黑色的。

针对违反性质4)

①z的叔叔y是红色的

②z的叔叔y是黑色的,而且z是右孩子

③z的叔叔y是黑色的,而且z是左孩子
z的叔叔y是黑色的。这两种情况是通过z是p[z]的左孩子还是右孩子来区别。在情况2中,结点z是他的父亲的右孩子。我们立即使用一个左旋来将此情况转变为情况3,此时结点z成为左孩子。因为z和p[z]都是红色的,所以所做的旋转对结点的黑高度和性质5)都无影响。在情况3中,要改变某些结点的颜色,并作一次右旋以保持性质5。这样,由于在一行中不在有两个连续的红色结点,因而,所有的处理完毕,无需再次执行while循环。

二叉查找树删除

*二叉查找树删除(删除结点z)

如果z没有子女,则修改其父结点p[z],使NIL为其子女;

如果结点z只有一个子女,则可以通过在其子结点与父结点间建立一条链来删除z;

如果结点z有两个子女,先删除z的后继y,再用y的内容替代z的内容。

*后继

二叉树中,如果所有的关键字均不相同,则某一结点x的后继既具有大于key[x]中的关键字中最小者的那个结点。

红黑树删除

RB-DELETE(T,z)

1 if left[z] = nil[T] or right[z] = nil[T]

2 then y ← z

3 else y ← TREE-SUCCESSOR(z)

4 if left[y] ≠ nil[T]

5 then x ← left[y]

6 else x ← right[y]

7 p[x] ← p[y]

8 if p[y] = nil[T]


9 then root[T] ← x

10 else if y = left[p[y]]

11 then left[p[y]] ← x

12 else right[p[y]] ← x

13 if y ≠ z

14 then key[z] ← key[y]

15 copy y’s satellite data into z

16 if color[y] = BLACK

17 then RB-DELETE-FIXUP(T, x)

18 return y

A.如果被删除的结点是红色的,则当结点被删除后,红黑性质仍然得以保持,理由如下:

a)树中各结点的黑高度都没有变化

b)不存在两个相邻的红色结点

c)因为如果该节点是红的,就不可能是根,所以跟仍然是黑色的

B.如果被删除的结点是黑色的,则会产生三个问题。

要删除的结点y,如果y有个不是NIL的孩子,则x为y的唯一孩子;如果y没有孩子,则x为NIL,把x的父节点(原来是y)赋值为y的父节点

①如果y原来是根结点,而y的一个红色的孩子成为了新的根,这就违反了性质2)。

②如果x和p[y](现在也是p[x])都是红的,就违反了性质4)。

③删除y将导致先前包含y的任何路径上黑结点个数少1。因此,性质5)被y的一个祖先破坏了。补救这个问题的一个办法就是把结点x视为还有额外的一重黑色。也就是说,如果将任意包含结点x的路径上黑结点的个数加1,则这种假设下,性质5)成立。当将黑节点y删除时,将其黑色“下推”至其子节点。现在问题就变为结点x可能既不是红,也不是黑,从而违反了性质1)。结点x是双重黑色或红黑,这就分别给包含x的路径上黑结点的贡献2个或1个。x的color属性仍然是red(如果x是红黑的)或者black(如果x是双重黑色)。换言之,一个结点额外的黑色反映在x指向它,而不是他的color属性。

删除算法

RB-DELETE-FIXUP(T, x)

1 while x ≠ root[T] and color[x] = BLACK

2 do if x = left[p[x]]

3 then w ← right[p[x]]

4 if color[w] = RED

5 then color[w] ← BLACK Case1

6 color[p[x]] = RED Case1

7 LEFT-ROTATE(T,p[x]) Case1

8 w ← right[p[x]] Case1

9 if color[right[w]] = BLACK and color[right[w]= BLACK

10 then color[w] ← RED Case2

11 x ← p[x] Case2

12 else if color[right[w]] = BLACK

13 then color[left[w]] ← BLACK Case3

14 color[w] ← RED Case3

15 RIGHT-ROTATE(T,w) Case3

16 w ← right[p[x]] Case3

17 color[w] ← color[p[x]] Case4

18 color[p[x]] ← BLACK Case4

19 color[right[w]] ← BLACK Case4

20 LEFT-ROTATE(T,p[x]) Case4

21 x ← root[T] Case4

22 else (same as then clause with “right” and “left” exchanged)

23 color[x] ← BLACK

第1-22行中while循环的目的是将额外的黑色沿树上移,直到:

1.x指向一个红黑结点,将x(单独)着为黑色。

2.X指向根,这时可以简单地消除那个额外的黑色,或者

3.作必要的旋转和颜色修改

情况1:x的兄弟w是红色的
Þ 因为w必须有黑色的孩子,我们可以改变w和p[x]颜色,再对p[x]做一次左旋,从而红黑性质得以继续保持。现在,x的新兄弟是旋转之前w的某个孩子,其颜色为黑色。这样,我们已经将情况1)转化为情况2)3)或4)了。
情况2:x的兄弟w是黑色的,而且w的两个孩子都是黑色的
Þ 因为w也是黑色的,故从x和w上去掉一重黑色,从而x只有一重黑色而w为红色。为了补偿从x和w中去掉一重黑色,我们想在原来是红色或者黑色的p[x]内新增一重额外的黑色。通过以p[x]为新结点x来恢复while循环。注意如果从情况1进入情况2,则新结点x是红黑色的,因为原来的p[x]是红色的。因此,新结点x的color属性的值c为red,并且在测试循环条件后循环结束。然后新结点x在第23行中被(单独)着为黑色。
情况3:x的兄弟w是黑色的,w的左孩子是红色的,右孩子是黑色的
Þ 可以交换w和其左孩子left[w]的颜色,并对w进行右旋,而红黑性质仍然保持。现在x的新兄弟w是一个有红色右孩子的黑节点,这样我们从情况3转换成情况4
情况4:x的兄弟w是黑色的,而且w的右孩子是红色的
Þ 通过做某些颜色修改并对p[x]做一次左旋,可以去掉x的额外黑色来把它变成单独黑色,而不破坏红黑性质。将x置为根后,当while循环测试其循环条件时循环结束。
了解更多

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics