平衡二叉树 之 红黑树

[原文链接]

1. 红黑树的特性

Red-Black Tree (  RBT)也是一种自平衡二叉树,其统计性能要好于 AVL树 。它是在1972年由 鲁道夫·贝尔 发明的,它现代的名字是在 Leo J. Guibas 和 Robert Sedgewick 于1978年写的一篇论文中获得的。它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的。[参考Wiki]

一般的,红黑树同时满足以下五大特性:

  1. 所有节点的颜色是红色或者黑色;
  2. 根节点是黑色;
  3. 所有的叶子节点是黑色(叶子节点包含NULL);
  4. 每个红色的节点都有两个黑色的子节点;
  5. 从任意节点出发,到其所有叶子节点的简单路径上都包含相同数目的黑色节点.

从上面的性质 4 和 5来看,红色节点和黑色节点基本上是交替的出现,所以红黑树从根到叶子的最长的可能路径不多于最短的可能路径的两倍长,这样的结果是这个树大致上是平衡的。因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的。

2. 数据结构定义

RBT数据结构在基本二叉树数据结构之上增加一个color和parent,color用于保存节点颜色,parent指向父节点。

[cpp] view plaincopyprint?

  1. #define COLOR_RED 0
  2. #define COLOR_BLACK 1
  3. typedef int keyType;
  4. // 定义而二叉树节点数据结构
  5. struct BinaryTreeNode {
  6. keyType key;
  7. int color;
  8. BinaryTreeNode* parent; // 保存父节点
  9. BinaryTreeNode* left; // left child
  10. BinaryTreeNode* right; // right child
  11. };
  12. // define red-black tree node
  13. typedef BinaryTreeNode rbnode;
  14. // define red-black tree
  15. typedef BinaryTreeNode rbtree;

3. 插入Key

无论怎么样操作,性质1和性质3是始终能够保持的。新插入节点的时候,新节点的初始颜色为红色,这样可以不直接破坏性质5,这个时候可能性质4受到威胁,需要调整节点颜色或这左一些旋转等操作。假设新插入的节点为N,其父节点为P,祖父节点为G,叔父节点为U,下面具体分析一下插入新节点的各种情况。

情形1 、空树

当树为空的时候,直接将N节点设为黑色作为树的根节点返回。

情形2 、P为黑色节点

图中所示为N插入到P左孩子节点中,这个过程完全满足性质 1 - 5 的要求,并没有破坏 RBT 的规则,因此,此时即可停止检查,插入过程结束。

同理,若P为黑色节点,N插入后作为P的右孩子节点也不会破坏 RBT的规则。

(下面开始讨论 P 为红色 的情形,由 性质2 推导出 G 一定存在,根据性质 4,G一定是黑色)

情形3 、P为红色节点,U存在且为红色节点

这种情况下,将G变为红色,P和U变为黑色,这样维持了G为Root的子树内性质4和5,然后以G作为新的插入节点,从情形1开始迭代。

情形4 、P为红色节点,U不存在或者U为黑色

(a) 若P在G的左侧,插入点N也在P的左侧 ------ LL 型
假设G的右侧有 路径有 x个黑色节点,则c有x个黑色节点,G为root的子树路径有x+1个黑色节点。 此时, 只需要以P为中心右旋,将P变为黑色,G变为红色,G的左子树替换为c,这样就可以继续保证以P为root的子树有x+1个黑色节点,检查停止。
(b)若P在G的左侧,N在P的右侧  -----LR型
这时候先将节点P和节点N做调整,进行左旋,变化成(a)的形态,然后做一次右旋。到此,调整完毕。
图略 (c)若P在G的右侧,N在P的右侧 ----------RR型
情形和(a)正好相反,做一次左旋即可。
图略 (d)若P在G的右侧,N在P的左侧 ----------RL型
情形和(b)正好相反,先做做一次右旋,后进行一次左旋即可。

RBT插入算法过程代码如下:

[cpp] view plaincopyprint?

  1. // 向右旋转
  2. void rb_right_rotate(rbnode* g) {
  3. rbnode * p = g->left;
  4. keyType k = g->key;
  5. g->key = p->key;
  6. p->key = k;
  7. g->left = p->left;
  8. if (NULL != p->left) {
  9. p->left->parent = g;
  10. }
  11. p->left = p->right;
  12. p->right = g->right;
  13. if (NULL != g->right) {
  14. g->right->parent = p;
  15. }
  16. g->right = p;
  17. }
  18. // 向左旋转
  19. void rb_left_rotate(rbnode* g) {
  20. rbnode* p = g->right;
  21. keyType k = g->key;
  22. g->key = p->key;
  23. p->key = k;
  24. g->right = p->right;
  25. if (NULL != p->right) {
  26. p->right->parent = g;
  27. }
  28. p->right = p->left;
  29. p->left = g->left;
  30. if (NULL != g->left) {
  31. g->left->parent = p;
  32. }
  33. g->left = p;
  34. }
  35. // check and adjust after insertion
  36. void rbt_insert_check(rbnode* node) {
  37. // CASE 1 :  if the node equals the root
  38. // set the color of the node black and return.
  39. if (NULL == node->parent) {
  40. node->color = COLOR_BLACK;
  41. return;
  42. }
  43. // CASE 2 : when the parent of the node is black
  44. // All features have been met, stop check and return
  45. if (node->parent->color == COLOR_BLACK) {
  46. return;
  47. }
  48. // Otherwise, the the parent node is RED, and this means the grandfather node exists.
  49. rbnode* gf = node->parent->parent;
  50. rbnode* uf = (gf->left == node->parent) ? gf->right : gf->left;
  51. // CASE 3 : When the uncle node exists and it‘s RED
  52. if (NULL != uf && uf->color == COLOR_RED) {
  53. // set parent and uncle black, set grandfather red
  54. node->parent->color = COLOR_BLACK;
  55. uf->color = COLOR_BLACK;
  56. gf->color = COLOR_RED;
  57. // then re check the tree at grandfather node from CASE 1.
  58. rbt_insert_check(gf);
  59. return;
  60. }
  61. // CASE 4 : when the uncle is NULL or its color is Black.
  62. if (node->parent == gf->left) { // the node in the left of its grandfather
  63. // (a) LL model
  64. if (node == node->parent->left) { // the node in the left of its parent
  65. rb_right_rotate(gf);
  66. }
  67. // (b) LR model
  68. else if (node == node->parent->right) { //the node in the right of its parent.
  69. rb_left_rotate(node->parent);
  70. rb_right_rotate(gf);
  71. }
  72. } else if (node->parent == gf->right) { //the node in the right of its grandfather
  73. // (c) RR model
  74. if (node == node->parent->right) { //the node in the right of its parent.
  75. rb_left_rotate(gf);
  76. }
  77. // (d) RL model
  78. else if (node == node->parent->left) { //the node in the left of its parent.
  79. rb_right_rotate(node->parent);
  80. rb_left_rotate(gf);
  81. }
  82. }
  83. }
  84. // 插入新的关键字
  85. int rbt_insert(rbtree* &tree, keyType key) {
  86. if (NULL == tree) { // if the tree is NULL
  87. tree = (rbtree*) malloc((sizeof(rbnode)));
  88. tree->key = key;
  89. tree->color = COLOR_BLACK;
  90. tree->parent = tree->left = tree->right = NULL;
  91. return 1;
  92. }
  93. // find insert point
  94. rbnode *n = tree, *p = tree->parent;
  95. while (NULL != n) {
  96. if (key == n->key) {
  97. return 0;
  98. }
  99. p = n;
  100. n = (key > p->key) ? p->right : p->left;
  101. }
  102. // insert the node
  103. n = (rbtree*) malloc((sizeof(rbnode)));
  104. n->key = key;
  105. n->color = COLOR_RED;
  106. n->parent = p;
  107. n->right = n->left = NULL;
  108. ((key > p->key) ? p->right : p->left) = n;
  109. // adjust the tree
  110. rbt_insert_check(n);
  111. return 1;
  112. }

4.删除Key

从RBT中删除指定的Key时,需要重新调整树的形态使之满足红黑树的特性。下面来具体分析一下删除Key的过程。

(1)根据key找到要删除的节点Node:如果没有找到,直接返回,否则,进行下一步操作;

(2)如果Node有两个孩子节点,那么删除Node之后该如何放置其孩子节点呢?

这个时候需要进行预处理,转化为删除只有一个孩子的节点的情形。

找到Node的中序前驱(后继)节点,将前驱(后继)节点的值复制到Node中,Node指向前驱(后继)节点;

(3)到此步骤,Node确切的指示为待删除的节点,且Node最多只有一个孩子节点。

删除Node节点,将Node的孩子节点顶替Node的位置.(注意Node为Root的情形)

(4)调整RBT树使其满足规定的5大特性。
           假设上一步中顶替上来的节点为 N ,其父节点为 P ,其兄弟节点为 S ,Sl 和 Sr 分别为 S 的左右孩子节点 ,假设 N 在 P 的左侧, 调整过程如下:
          (右侧与左侧对称,这里分析一种即可)

情形1 、N节点为红色

当N节点为红色的时候,由于左侧缺少一个黑色的节点,可以将N节点的颜色修改为黑色,这样即可从新满足性质5.

调整完毕。

情形2、S节点为红色

当S节点为红色节点时,则可以将P节点向左旋转,旋转之后P为红色,S为黑色,这个时候S-Sl这条简单路径黑色节点数目合法,S-P-Sl节点数目也合法,S-P-N路径黑色节点数目少一个。

相当于,P的左侧删除了一个黑色节点,应当重新调整 P,S,Sl,Sr所指向的节点,进行后续操作。
后续可能的情形为:3,5,6

情形3、P节点为红色,S,Sl,Sr为黑色

当P为红色,S为黑色,Sl和Sr均为黑色的时候,则可以简单的交换P节点和S节点的颜色,这样即可使各条简单路径的黑色节点数目和删除节点前相等。

调整完毕。

情形4、P,S,Sl,Sr均为黑色

当P、S、Sl、Sr均为黑色节点的时候,只需要简单的将S节点标记为红色,这样以P节点为根的个简单路径黑色节点数目比删除之前少一个。

因此,P相当与N的位置,从P的父节点开始递归进行调整。
(如果此时P为树的根节点,即可停止调整)

情形5、Sl为红色节点并且Sr为黑色

这种情况下,可以将Sl进行右旋操作,右旋之后,Sl为黑色,S为红色,Sr不变,这样保持P节点右子树中各简单路径黑色节点数目和旋转前一样。
这个时候,原来的S相当于Sl的位置,Sl相当与a,Sr相当与S。

更新S,Sl,Sr新指向的位置,进行下一步操作。
后续情形为:6.

情形6、Sr为红色

这时,将P节点做一次左旋操作,将Sr的颜色设置为黑色,P和S交换颜色,调整之后,各简单路径的黑色节点数目和删除节点之前一样。

此时调整结束。

[cpp] view plaincopyprint?

  1. int is_black(rbnode * node) {
  2. if (node == NULL) return 1;
  3. if (node->color == COLOR_BLACK) return 1;
  4. return 0;
  5. }
  6. // check and adjust after deletion
  7. void rbt_delete_check(rbnode* p, bool delLeft) {
  8. rbnode * n = delLeft ? p->left : p->right;
  9. // case 1: n is red
  10. if (NULL != n && n->color == COLOR_RED) {
  11. n->color = COLOR_BLACK;
  12. return;
  13. }
  14. // else the other subtree of p at least has one more node.
  15. rbnode * s = delLeft ? p->right : p->left;
  16. rbnode * sl = s->left;
  17. rbnode * sr = s->right;
  18. // case 2 : S is red , p left rotate
  19. if (s->color == COLOR_RED) {
  20. if (delLeft) {
  21. rb_left_rotate(p);
  22. } else {
  23. rb_right_rotate(p);
  24. }
  25. p = s;
  26. s = delLeft ? sl : sr;
  27. sl = s->left;
  28. sr = s->right;
  29. }
  30. // Other cases : S is black
  31. // when SL and SR  are black
  32. if (is_black(sl) && is_black(sr)) {
  33. // case 3 : P is red,  S SL and SR are black
  34. if (!is_black(p)) {
  35. p->color = COLOR_BLACK;
  36. s->color = COLOR_RED;
  37. }
  38. // case 4: P ,S, SL and SR are black
  39. else {
  40. s->color = COLOR_RED;
  41. if (NULL == p->parent) {
  42. return;
  43. }
  44. delLeft = (p == p->parent->left);
  45. rbt_delete_check(p->parent, delLeft);
  46. }
  47. return;
  48. }
  49. // when SL and SR has red node
  50. if (delLeft) {
  51. if (is_black(sr)) {    // case 5(a) : delLeft is true and SR is black
  52. rb_right_rotate(s);
  53. sr = s->right;
  54. }
  55. rb_left_rotate(p);    // case 6(a) : rotate the p node
  56. sr->color = COLOR_BLACK;
  57. } else {
  58. if (is_black(sl)) {    // case 5(b) : delLeft is false and SL is black
  59. rb_left_rotate(s);
  60. sl = s->left;
  61. }
  62. rb_right_rotate(p);    // case 6(b) : rotate the p node
  63. sl->color = COLOR_BLACK;
  64. }
  65. }
  66. // delete a key from the RBT
  67. int rbt_delete(rbtree* &tree, keyType key) {
  68. if (NULL == tree) {
  69. return 0;
  70. }
  71. // find the node
  72. rbnode *curr, *temp;
  73. for (curr = tree;;) {
  74. if (key == curr->key) {
  75. break;
  76. }
  77. curr = (key > curr->key) ? curr->right : curr->left;
  78. if (NULL == curr) {
  79. return 0;
  80. }
  81. }
  82. // if the node to delete has two children
  83. if (NULL != curr->left && NULL != curr->right) {
  84. for (temp = curr->left; NULL != temp->right; temp = temp->right) {
  85. }
  86. curr->key = temp->key;
  87. curr = temp;
  88. }
  89. if (NULL == curr->parent) { // it is the tree root
  90. tree = (NULL == curr->left) ? curr->right : curr->left;
  91. if (tree != NULL) {
  92. tree->color = COLOR_BLACK;
  93. tree->parent = NULL;
  94. }
  95. free(curr);
  96. return 1;
  97. }
  98. // delete the node
  99. rbnode* fa = curr->parent;
  100. temp = (NULL == curr->left) ? curr->right : curr->left;
  101. bool delLeft = (fa->left == curr);
  102. if (NULL != temp) {
  103. temp->parent = fa;
  104. }
  105. delLeft ? fa->left = temp : fa->right = temp;
  106. if (curr->color != COLOR_RED) { // adjust after deletion
  107. rbt_delete_check(fa, delLeft);
  108. }
  109. free(curr);
  110. return 1;
  111. }
时间: 2024-11-05 22:56:13

平衡二叉树 之 红黑树的相关文章

平衡二叉树和红黑树最差情况性能分析

平衡二叉树和红黑树最差情况分析 1.经典平衡二叉树 平衡二叉树(又称AVL树)是带有平衡条件的二叉查找树,使用最多的定理为:一棵平衡二叉树是其每个节点的左子树和右子树的高度最多差为1的二叉查找树.因为他是二叉树的一种具体应用,所以他同样具有二叉树的性质.例如,一棵满二叉树在第k层最多可拥有个节点(性质1).一棵树的高度为其从根节点到最底层节点经过的路径数(例如只含一个节点的树的高度为0)(性质2).并且已被证明,一棵含有N个节点的平衡二叉树的高度最多(粗略来说)为. 下面我们来尝试总结如何得到一

二叉查找树、平衡二叉树和红黑树

首先说一下,凡是每个节点最多只有两个子节点的树都叫二叉树. 二叉查找树 二叉查找树,也称二叉搜索树,或二叉排序树.其定义也比较简单,要么是一颗空树,要么就是具有如下性质的二叉树: (1)若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值: (2) 若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值: (3) 任意节点的左.右子树也分别为二叉查找树: (4) 没有键值相等的节点. 平衡二叉树(AVL二叉树) ? 平衡二叉搜索树,又被称为AVL树,且具有以下性质:它是

TreeMap源码分析之一 —— 排序二叉树、平衡二叉树、红黑树

一.排序二叉树(BST树) 1.排序二叉树的定义 排序二叉树,Binary Sort Tree 排序二叉树要么是一棵空二叉树,要么是具有下列性质的二叉树: (1)若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值: (2)若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值: (3)它的左.右子树也分别为排序二叉树. 按中序遍历排序二叉树可以得到由小到大的有序序列. 比如 2.排序二叉树的插入和删除 二.平衡二叉树(Balanced Binary Tree,AVL树) 三.红黑

排序二叉树,平衡二叉树和红黑树的概念以及相关的操作讲解

1. 排序二叉树     排序二叉树是一种特殊结构的二叉树,可以非常方便地对树中所有节点进行排序和检索. 排序二叉树要么是一棵空二叉树,要么是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值: 若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值: 它的左.右子树也分别为排序二叉树. 图 1 显示了一棵排序二叉树: 图 1. 排序二叉树 对排序二叉树,若按中序遍历就可以得到由小到大的有序序列.如图 1 所示二叉树,中序遍历得: {2,3,4,8,9,9

树:BST、AVL、红黑树、B树、B+树

我们这个专题介绍的动态查找树主要有: 二叉查找树(BST),平衡二叉查找树(AVL),红黑树(RBT),B~/B+树(B-tree).这四种树都具备下面几个优势: (1) 都是动态结构.在删除,插入操作的时候,都不需要彻底重建原始的索引树.最多就是执行一定量的旋转,变色操作来有限的改变树的形态.而这些操作所付出的代价都远远小于重建一棵树.这一优势在<查找结构专题(1):静态查找结构概论 >中讲到过. (2) 查找的时间复杂度大体维持在O(log(N))数量级上.可能有些结构在最差的情况下效率将

红黑树(Red Black Tree)

介绍另一种平衡二叉树:红黑树(Red Black Tree),红黑树由Rudolf Bayer于1972年发明,当时被称为平衡二叉B树(symmetric binary B-trees),1978年被Leonidas J. Guibas 和 Robert Sedgewick改成一个比较摩登的名字:红黑树. 红黑树和之前所讲的AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能.自从红黑树出来后,AVL树就被放到了博物馆里,据说是红黑树有更好的效率,更高

Atitit 常见的树形结构 红黑树 &#160;二叉树 &#160;&#160;B树 B+树 &#160;Trie树&#160;attilax理解与总结

Atitit 常见的树形结构 红黑树  二叉树   B树 B+树  Trie树 attilax理解与总结 1.1. 树形结构-- 一对多的关系1 1.2. 树的相关术语: 1 1.3. 常见的树形结构 红黑树  二叉树   B树 B+树  Trie树2 1.4. 满二叉树和完全二叉树..完全二叉树说明深度达到完全了.2 1.5. 属的逻辑表示 树形比奥死,括号表示,文氏图,凹镜法表示3 1.6. 二叉树是数据结构中一种重要的数据结构,也是树表家族最为基础的结构.3 1.6.1. 3.2 平衡二叉

数据结构-红黑树详解

介绍: 红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组. 它是在1972年由Rudolf Bayer发明的,当时被称为平衡二叉B树(symmetric binary B-trees).后来,在1978年被 Leo J. Guibas 和 Robert Sedgewick 修改为如今的"红黑树". 红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能. 它虽然

二叉查找树(BST),平衡二叉查找树(AVL),红黑树(RBT),B~/B+树(B-tree)的比较

http://www.iteye.com/topic/614070 此少侠总结的特棒,直接收藏了. 我们这个专题介绍的动态查找树主要有: 二叉查找树(BST),平衡二叉查找树(AVL),红黑树(RBT),B~/B+树(B-tree).这四种树都具备下面几个优势: (1) 都是动态结构.在删除,插入操作的时候,都不需要彻底重建原始的索引树.最多就是执行一定量的旋转,变色操作来有限的改变树的形态.而这些操作所付出的代价都远远小于重建一棵树.这一优势在<查找结构专题(1):静态查找结构概论 >中讲到