技术分享会旨在:抛砖引玉,促进程序之间互相交流,培养公司内部良好的技术氛围。
1. Base
此次分享会默认大家清楚了解以下知识点:
- 二叉查找树(binary search tree)的定义和实现
 - AVL树的定义和实现
 - 基础的C/C++知识
 
此次分享会主要和大家分享探讨以下内容
- AVL树和红黑树的异同
 - 树的旋转
 - 红黑树的在STL中的应用(SGI STL)
 - 红黑树在跳跃表中的应用
 
2. AVL树和红黑树的性质
###AVL树
- 高度为h的AVL树最少有S(h) = S(h-1) + S(h-2) + 1个节点  
- $$S(h) = \frac{1}{\sqrt{5}}((\frac{1+\sqrt{5}}{2})^{h+2} - (\frac{1-\sqrt{5}}{2})^{h+2}) - 1$$
 - $$\because \frac{(\frac{1-\sqrt{5}}{2})^{h+2}}{\sqrt(5)} < 1 \therefore S(h) > \frac{(\frac{1+\sqrt{5}}{2})^{h+2}}{\sqrt(5)} - 1$$
 
 - AVL树的高度不超过$$\frac{3}{2}log_2^N$$
 - 查找时间复杂度: $$O(log_2^N)$$
 - 插入时间复杂度: $$O(log_2^N)$$+0-2次旋转
 - 删除时间复杂度: $$O(log_2^N)$$+若干次旋转
删除操作最多会造成$$O(log_2^N)$$次旋转,这种情况发生在删除最简AVL树的一个节点时发生。
 
###红黑树
- 红黑树的高度不超过$$2log_2^N$$
 - 查找时间复杂度: $$O(log_2^N)$$
 - 插入时间复杂度: $$O(log_2^N)$$+0-2次旋转
 - 删除时间复杂度: $$O(log_2^N)$$+0-3次旋转
所有的AVL树都能不经旋转涂成红黑树,反之不行。
 
###红黑树和AVL树对比
结论:红黑树和AVL树时间复杂度是一样的,但是红黑树的统计性能更高!
以下是对它们处理百万随机数的性能统计
##3. 红黑树的插入和删除
插入
我参考的是SGI STL版本的红黑数实现,源码如下:
__rb_tree_rebalance(__rb_tree_node_base* x, __rb_tree_node_base*& root)
{
    //参数1为新增节点
    x->color == __rb_tree_red; //新增节点必须为红色
    while(x != root && x->parent->color == __rb__tree_red)
    {
        if(x->parent == x->parent->parent->left)
        {
            //父节点为组父节点的左节点
            __rb_tree_node_base* y = x->parent->parent->right; //令y为伯父节点
            if(y && y->color == __rb_tree_red)
            {
                //情况1
                //伯父节点存在并且为红色
                x->parent->color = __rb_tree_black;
                y->color = __rb_tree_black;
                x->parent->parent->color = __rb_tree_red;
                x = x->parent->parent; //上滤
            }
            else
            {
                //无伯父节点,或伯父节点为黑
                if(x == x->parent->right)
                {
                    //情况2
                    x = x->parent;
                    __rb_tree_rotate_left(x, root);
                }
                //情况3
                x->parent->color = __rb_tree_black;
                x->parent->parent->color = __rb_tree_red;
                __rb_tree_rotate_right(x->parent->parent, root);
            }
        }
        else
        {
            __rb_tree_node_base* y = x->parent->parent->left; //令y为伯父节点
            if(y && y->color == __rb_tree_red)
            {
                //情况4
                //伯父节点存在并且为红色
                x->parent->color = __rb_tree_black;
                y->color = __rb_tree_black;
                y->parent->parent->color = __rb_tree_red;
                x = x->parent->parent; //上滤
            }
            else
            {
                //无伯父节点,或伯父节点为黑
                if(x == x->parent->left)
                {
                    //情况5
                    x = x->parent;
                    __rb_tree_rotate_right(x, root);
                }
                //情况6
                x->parent->color = __rb_tree_black;
                x->parent->parent->color = __rb_tree_red;
                __rb_tree_left(x->parent->parent, root);
            }
        }
    }
    root->color = __rb_tree_black;
}
- ####左旋转动画

 - ####右旋转动画

 - ####情况1
   
直接把父节点和伯父节点改成黑色,祖父节点改成红色,继续上滤就可以了。
- ####情况2
   
把子节点丰富
  
先对XP做一次左旋转
  
再对GX做一次右旋转并改变GX的颜色,SGI STL中提前改变了颜色,这是为了代码统一,不影响逻辑。
  
####情况3

####情况4

其实这种情况和情况1是相同的。
- ####情况5
   
做一次左旋转
- ####情况6
   
做一次右左双旋转
升序插入红黑树

降序插入红黑树

随机插入红黑树

###删除
删除红色节点
删除红色节点不影响红黑树的性质,可直接删除!
删除黑色节点
将右子节点的最左端节点代替该节点,然后进行reBlance。
如果后继节点为红色,则直接改成黑色,不需要reBlance。
经过节点A的所有路径长度都减少了1,reBlance过程中把这些情况分为以下4种。
节点A为删除后的替代节点,节点W为节点A的兄弟节点。
if (y->color != __rb_tree_red)
{   
    while (x != root && (x == 0 || x->color == __rb_tree_black))  
      if (x == x_parent->left)
      {  
        __rb_tree_node_base* w = x_parent->right;  
        //情况1  
        if (w->color == __rb_tree_red)
        {  
          w->color = __rb_tree_black;  
          x_parent->color = __rb_tree_red;  
          __rb_tree_rotate_left(x_parent, root);  
          w = x_parent->right;  
        }  
        //情况2  
        if ((w->left == 0 || w->left->color == __rb_tree_black) &&  
            (w->right == 0 || w->right->color == __rb_tree_black))
        {  
          w->color = __rb_tree_red;  
          x = x_parent;  
          x_parent = x_parent->parent;  
        }   
        else   
        {  
          //情况3  
          if (w->right == 0 || w->right->color == __rb_tree_black)
          {  
            if (w->left) w->left->color = __rb_tree_black;  
            w->color = __rb_tree_red;  
            __rb_tree_rotate_right(w, root);  
            w = x_parent->right;  
          }  
          //情况4  
          w->color = x_parent->color;  
          x_parent->color = __rb_tree_black;  
          if (w->right) w->right->color = __rb_tree_black;  
          __rb_tree_rotate_left(x_parent, root);  
          break;  
        }  
      }  
      if (x) x->color = __rb_tree_black;  
    }
- ####情况1

W为红色,AW的父节点为黑色。
对BD进行一次左旋,使得情况1转换成情况234中的一种。 - ####情况2

W和它两个子节点都为黑色
把W涂成红色,使得A和W两个子树路径长度都-1。
如果new x即B节点为红色,那么涂成黑色,就平衡了B节点的路径长。否则就需要上滤,继续平衡new x和其兄弟节点。 - ####情况3

W为黑色,W的左节点为红色,右节点为黑色。
对WC进行一次右旋,转换为情况4 - ####情况4

W为黑色,右子节点为红色。
对BD进行左旋,并交换颜色,再把W的右节点置为黑色。 
查找
while(x != 0)
    if(!key_compare(key(x), k))
        //x键值大于k
        y = x, x = left(x);
    else
        x = right(x);
iterator j = iterator(y);
//k的值比树中最大值都大,或者没有找到,则返回end()。  
//例如在root=10 10->left=8 10->right=14 14->left=11中查找12  
return (j == end() || key_compare(k, key(j.node())) ? end() : j;