平衡二叉树 之 红黑树

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指向父节点。

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

typedef int keyType;

// 定义而二叉树节点数据结构
struct BinaryTreeNode {
	keyType key;	
	int color;
	BinaryTreeNode* parent; // 保存父节点
	BinaryTreeNode* left; // left child
	BinaryTreeNode* right; // right child
};

// define red-black tree node
typedef BinaryTreeNode rbnode;

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

// 向左旋转
void rb_left_rotate(rbnode* g) {
    rbnode* p = g->right;
    keyType k = g->key;
    g->key = p->key;
    p->key = k;
    g->right = p->right;
    if (NULL != p->right) {
        p->right->parent = g;
    }
    p->right = p->left;
    p->left = g->left;
    if (NULL != g->left) {
        g->left->parent = p;
    }
    g->left = p;
}

// check and adjust after insertion
void rbt_insert_check(rbnode* node) {
    // CASE 1 :  if the node equals the root
    // set the color of the node black and return.
    if (NULL == node->parent) {
        node->color = COLOR_BLACK;
        return;
    }
    // CASE 2 : when the parent of the node is black
    // All features have been met, stop check and return
    if (node->parent->color == COLOR_BLACK) {
        return;
    }

    // Otherwise, the the parent node is RED, and this means the grandfather node exists.
    rbnode* gf = node->parent->parent;
    rbnode* uf = (gf->left == node->parent) ? gf->right : gf->left;

    // CASE 3 : When the uncle node exists and it's RED
    if (NULL != uf && uf->color == COLOR_RED) {
        // set parent and uncle black, set grandfather red
        node->parent->color = COLOR_BLACK;
        uf->color = COLOR_BLACK;
        gf->color = COLOR_RED;
        // then re check the tree at grandfather node from CASE 1.
        rbt_insert_check(gf);
        return;
    }

    // CASE 4 : when the uncle is NULL or its color is Black.
    if (node->parent == gf->left) { // the node in the left of its grandfather
        // (a) LL model
        if (node == node->parent->left) { // the node in the left of its parent
            rb_right_rotate(gf);
        }
        // (b) LR model
        else if (node == node->parent->right) { //the node in the right of its parent.
            rb_left_rotate(node->parent);
            rb_right_rotate(gf);
        }
    } else if (node->parent == gf->right) { //the node in the right of its grandfather
        // (c) RR model
        if (node == node->parent->right) { //the node in the right of its parent.
            rb_left_rotate(gf);
        }
        // (d) RL model
        else if (node == node->parent->left) { //the node in the left of its parent.
            rb_right_rotate(node->parent);
            rb_left_rotate(gf);
        }
    }
}

// 插入新的关键字
int rbt_insert(rbtree* &tree, keyType key) {
    if (NULL == tree) { // if the tree is NULL
        tree = (rbtree*) malloc((sizeof(rbnode)));
        tree->key = key;
        tree->color = COLOR_BLACK;
        tree->parent = tree->left = tree->right = NULL;
        return 1;
    }
    // find insert point
    rbnode *n = tree, *p = tree->parent;
    while (NULL != n) {
        if (key == n->key) {
            return 0;
        }
        p = n;
        n = (key > p->key) ? p->right : p->left;
    }
    // insert the node
    n = (rbtree*) malloc((sizeof(rbnode)));
    n->key = key;
    n->color = COLOR_RED;
    n->parent = p;
    n->right = n->left = NULL;
    ((key > p->key) ? p->right : p->left) = n;

    // adjust the tree
    rbt_insert_check(n);

    return 1;
}

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交换颜色,调整之后,各简单路径的黑色节点数目和删除节点之前一样。

此时调整结束。
  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.   
  7. // check and adjust after deletion   
  8. void rbt_delete_check(rbnode* p, bool delLeft) {  
  9.     rbnode * n = delLeft ? p->left : p->right;  
  10.     // case 1: n is red   
  11.     if (NULL != n && n->color == COLOR_RED) {  
  12.         n->color = COLOR_BLACK;  
  13.         return;  
  14.     }  
  15.     // else the other subtree of p at least has one more node.   
  16.     rbnode * s = delLeft ? p->right : p->left;  
  17.     rbnode * sl = s->left;  
  18.     rbnode * sr = s->right;  
  19.   
  20.     // case 2 : S is red , p left rotate   
  21.     if (s->color == COLOR_RED) {  
  22.         if (delLeft) {  
  23.             rb_left_rotate(p);  
  24.         } else {  
  25.             rb_right_rotate(p);  
  26.         }  
  27.         p = s;  
  28.         s = delLeft ? sl : sr;  
  29.         sl = s->left;  
  30.         sr = s->right;  
  31.     }  
  32.     // Other cases : S is black   
  33.     // when SL and SR  are black   
  34.     if (is_black(sl) && is_black(sr)) {  
  35.         // case 3 : P is red,  S SL and SR are black   
  36.         if (!is_black(p)) {  
  37.             p->color = COLOR_BLACK;  
  38.             s->color = COLOR_RED;  
  39.         }  
  40.         // case 4: P ,S, SL and SR are black   
  41.         else {  
  42.             s->color = COLOR_RED;  
  43.             if (NULL == p->parent) {  
  44.                 return;  
  45.             }  
  46.             delLeft = (p == p->parent->left);  
  47.             rbt_delete_check(p->parent, delLeft);  
  48.         }  
  49.         return;  
  50.     }  
  51.     // when SL and SR has red node   
  52.     if (delLeft) {  
  53.         if (is_black(sr)) {    // case 5(a) : delLeft is true and SR is black   
  54.             rb_right_rotate(s);  
  55.             sr = s->right;  
  56.         }  
  57.         rb_left_rotate(p);    // case 6(a) : rotate the p node   
  58.         sr->color = COLOR_BLACK;  
  59.     } else {  
  60.         if (is_black(sl)) {    // case 5(b) : delLeft is false and SL is black   
  61.             rb_left_rotate(s);  
  62.             sl = s->left;  
  63.         }  
  64.         rb_right_rotate(p);    // case 6(b) : rotate the p node   
  65.         sl->color = COLOR_BLACK;  
  66.     }  
  67. }  
  68.   
  69. // delete a key from the RBT   
  70. int rbt_delete(rbtree* &tree, keyType key) {  
  71.     if (NULL == tree) {  
  72.         return 0;  
  73.     }  
  74.     // find the node   
  75.     rbnode *curr, *temp;  
  76.     for (curr = tree;;) {  
  77.         if (key == curr->key) {  
  78.             break;  
  79.         }  
  80.         curr = (key > curr->key) ? curr->right : curr->left;  
  81.         if (NULL == curr) {  
  82.             return 0;  
  83.         }  
  84.     }  
  85.     // if the node to delete has two children   
  86.     if (NULL != curr->left && NULL != curr->right) {  
  87.         for (temp = curr->left; NULL != temp->right; temp = temp->right) {  
  88.         }  
  89.         curr->key = temp->key;  
  90.         curr = temp;  
  91.     }  
  92.     if (NULL == curr->parent) { // it is the tree root   
  93.         tree = (NULL == curr->left) ? curr->right : curr->left;  
  94.         if (tree != NULL) {  
  95.             tree->color = COLOR_BLACK;  
  96.             tree->parent = NULL;  
  97.         }  
  98.         free(curr);  
  99.         return 1;  
  100.     }  
  101.     // delete the node   
  102.     rbnode* fa = curr->parent;  
  103.     temp = (NULL == curr->left) ? curr->right : curr->left;  
  104.     bool delLeft = (fa->left == curr);  
  105.     if (NULL != temp) {  
  106.         temp->parent = fa;  
  107.     }  
  108.     delLeft ? fa->left = temp : fa->right = temp;  
  109.     if (curr->color != COLOR_RED) { // adjust after deletion   
  110.         rbt_delete_check(fa, delLeft);  
  111.     }  
  112.     free(curr);  
  113.     return 1;  
  114. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
因权限不够,只能上传20M,故分两部分上传 提供了有关使用算法和数据结构的一个详尽的介绍。Bucknall先从算法性能的讨论开始,涵盖了诸如数组、链表和二叉树等内容。这本书强调了查找算法(如顺序和二分查找),另外也重点介绍了排序算法(包括冒泡排序、插入排序、希尔排序、快速排序和堆排序),此外还提供了有关的优化技术。不仅如此,作者还介绍了散列和散列表、优先队列、状态机和正则表达式以及诸如哈夫曼和LZ77等数据压缩技术。 随附光盘中有作者所开发的一个相当成功的自由软件库EZDSL,另外还有可运行于各版本Delphi上和Kylix上的源代码,此外还提供了TurboPower Software公司的可执行程序。 目录 前言 致谢 第1章什么是算法 1.1什么是算法 1.2算法和平台 1.3调试与测试 1.4小结 第2章数组 2.1数组 2.2Delphi中的数组类型 2.3TList类和指针数组 2.4磁盘数组 2.5小结 第3章链表、栈和队列 3.1单链表 3.2双向链表 3.3链表的优缺点 3.4栈 3.5队列 3.6小结 .第4章查找 4.1比较例程 4.2顺序查找 4.3二分查找 4.4小结 第5章排序 5.1排序算法 5.2排序基础知识 5.3小结 第6章随机算法 6.1随机数生成 6.2其他随机数分布 6.3跳表 6.4小结 第7章散列和散列表 7.1散列函数 7.2利用线性探测方法实现冲突解决 7.3其他开放定址机制 7.4利用链式方法解决冲突 7.5利用桶式方法解决冲突 7.6磁盘上的散列表 7.7小结 第8章二叉树 8.1创建一个二叉树 8.2叉树的插入和删除 8.3二叉树的遍历 8.4二叉树的类实现 8.5二叉查找树 8.6伸展树 8.7红黑树 8.8小结 第9章 优先队列和堆排序 9.1优先队列 9.2堆 9.3堆排序 9.4扩展优先队列 9.5小结 第10章 状态机和正则表达式 10.1状态机 10.2正则表达式 10.3小结 第11章数据压缩 11.1数据表示 11.2数据压缩 11.3位流 11.4最小冗余压缩 11.5字典压缩 11.6小结 第12章 高级主题 12.1读者-写者算法 12.2生产者-消费者算法 12.3查找两文件的差别 12.4小结 后记

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值