二叉查找树 & 红黑树

二叉查找树 & 红黑树
//内容来自《算法导论》Chapter 12&13
//算法导论的电子版,在我的下载资源中有,
//附有答案
-----------------------------------------------------
- 二叉查找树性质:
  设x为二叉查找树的一个结点。如果y是x的左子树,则key[y] <=key[x]。
  如果y是x的右子树中的一个结点,则key[x]<=key[y]。
- 定理12.1
   如果x是一棵包含n个结点的子树的根,则调用INORDER-TREE_WALK(x)过程的时间为
   O(n)。
- 定理12.2
  对一棵高度为h的二叉树,动态集合操作SEARCH, MINIMUM, MAXIMUM, SUCCESSOR,
  PREDESSOR等的运行时间均为O(h)。
- 定理12.3
  对高度为h的二叉查找树,动态集合操作INSERT和DELETE的运行时间为O(h)。
- 定理12.4
  一棵在n个关键字上随机构造的二叉查找树的期望高度为O(lgn)。
- 中序遍历二叉查找树,可以按照排列顺序输出树中的所有关键字。

插入: 结点的插入操作永远在树的“最外层”进行。

删除:
/* 从T中,"删除" z结点 */
TREE-DELETE(T, z)
{
   /* y指向实际要删除结点 */
    if( left[z] == NIL || right[z] == NIL) { //如果z最多只有一个孩子
       y = z;
    } else { // 如果z有两个孩子
      y = TREE-SUCCESSOR(z);
    }
    
    /* x指向实际要删除结点y的一个孩子 */
    if(left[y] != NIL) {
       x = left[y];
    } else { // 注意:当z的左右孩子都不为空时 ,y只有右孩子,没有左孩子
      x = right[y];
    }
    
    /* 处理实际要删除结点的孩子的父指针 */
    if( x != NIL) {//排除了z为叶子结点的情况
       p[x] = p[y];//把实际要删除结点的孩子交给它的父亲
    }
    
    /* 处理实际要删除结点父亲的孩子指针 */
    if (p[y] = NIL) { //实际要删除的是根结点
      root[T] = x;
    } else if (y == left[p[y]]) {//y为其父结点的左孩子
      left[p[y]] = x;
    } else { //y为其父结点的右孩子
      right[p[y]] = x;
    }
    
    if (y != z) {//z的左右孩子都不为空
       key[z] = key[y];
       copy y's satelite data into z;
    }
}


Figure 12-4 删除操作


-------------------- 红黑树 Chapter 13
- 红黑树是“平衡的“查找树的一种,它能保证在最坏的情况下,基本动态集合操作的时间
 为O(lgn). (search, predecesor, successor, minimum, maximum, inseart, delete)
- 红黑树确保没有一条路径会比其他路径长出两倍,因而是接近“平衡”的。
- 树中每个结点包含五个域:color, key, left, right, p.
- 如果结点没有父结点或者没有子结点,则该结点相应的指针(p)域包含值NIL。
   我们把这些nul视为查找树的外结点(叶子)的指针,而把带关键字的结点视为树的内
   结点。
- 为了便于处理红黑树代码中的边界条件,我们采用一个哨兵(nil[T])代表NIL。
  所有的NIL被替换为指向nil[T]的指针。
- 我们绘的图中忽略了叶子结点。
- 从某个结点x出发(不包括该结点)到达一个叶子结点的任意一条路径上,黑色结点的
  个数称为该结点x的黑高度, 用bh(x)表示。红黑树的高度为根结点的黑高度。
--------------------
红黑树的性质:
 1. 每个结点或是红色的,或是黑色的
 2. 根结点是黑色的
 3. 每个叶子结点(NIL)是黑色的
 4. 如果一个结点是红色的,则它的两个儿子都是黑色的
 5. 对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点
-------------
- 引理13.1 一棵有n个内结点的红黑树的高度至多为2lg(n+1)。
-----
- 当红黑树的性质因为插入或者删除而被破坏,可以通过旋转和调整结点颜色来恢复。
   其中旋转可以同时也保证红黑树的二叉查找树的性质。
- 插入: 结点的插入操作永远在树的“最外层”内结点进行。

插入://z是被插入的结点
RB-INSERT-FIXUP(T, z)
{
  y = nil[T];
  x = root[T];
 
  /* 找到插入位置, */
  while( x != nil[T]) { //  y指向z要插入的父结点
    y = x;
    if (key[z] < key[x]) {
      x = left[x];
    } else {
      x = right[x];
    }
 } //while
    /* 插入 */
    p[z] = y;
    if (y == nil[T]) {
       root[T] = z;
    } else if (key[z] < key[y]) {
       left[y] =z;
    } else {
      right[y] = z;
    }
   left[z] = nil[T];
   right[z] = nil[T];
   color[z] = RED;
   /* 调整 */
   RB-INSERT-FIXUP(T, z);
}

RB-INSERT-FIXUP(T, z)
{
  while (color[p[z]] == RED){//z的父结点为红色
    if (p[z] == left[p[p[z]]]) {//如果z的父结点是z祖父结点的左孩子
      y = right[p[p[z]]]; // y指向z祖父结点的右孩子
      
      if (color[y] == RED){//z结点的(右)叔叔结点是红色的 <case 1>
        //以下的变换过程保持红黑树的性质
        color[p[z]] = BLACK;
        color[y] = BLACK;
        color[p[p[z]]] = RED;
        z = p[p[z]];//“问题指针”z上移
      } else  if (z == right[p[z]]) { // 如果z是其父结点的右孩子, z的(右)叔叔结点是黑色的<case 2>
        /* 转化为左孩子,进入 <case 3>*/
        z = p[z]; //“问题指针”z上移
        LEFT-ROTATE(T,z);
      }
       /* 如果z是其父结点的左孩子, z的(右)叔叔结点是黑色的<case 3>*/
       color[p[z]] = BLACK;
       color[p[p[z]]] = RED;
       RIGHT-ROTATE(T, p[p[z]]);
    }
  } else {//如果z的父结点是z祖父结点的右孩子
     same as then clause with "right" and "left" exchange;
  }
  color[root[T]] = BLACK;// 涂黑根部
}


- 上面的循环的每次迭代都会使得“问题指针”z上移,或者执行了某些旋转操作而结束
   循环。

Figure 13-5 && Figure 13-6 插入操作


--------------------
删除:
/* 从T中,"删除" z结点 */
RB-DELETE(T, z)
{
   /* y指向实际要删除结点 */
    if( left[z] == nil[T] || right[z] == nil[T]) { //如果z最多只有一个孩子
       y = z;
    } else { // 如果z有两个孩子
      y = TREE-SUCCESSOR(z);
    }
    
    /* x指向实际要删除结点y的一个孩子 */
    if(left[y] != nil[T]) {
       x = left[y];
    } else { // 注意:当z的左右孩子都不为空时 ,y只有右孩子,没有左孩子
      x = right[y];
    }
    
    /* 处理实际要删除结点的孩子的父指针 */
    p[x] = p[y];//把实际要删除结点的孩子交给它的父亲
    
    /* 处理实际要删除结点父亲的孩子指针 */
    if (p[y] = nil[T]) { //实际要删除的是根结点
      root[T] = x;
    } else if (y == left[p[y]]) {//y为其父结点的左孩子
      left[p[y]] = x;
    } else { //y为其父结点的右孩子
      right[p[y]] = x;
    }
    
    if (y != z) {//z的左右孩子都不为空
       key[z] = key[y];
       copy y's satelite data into z;
    }
    
    if (color[y] == BLACK) {//如果实际删除的是黑色结点,那么需要调整
       RB-DELETE-FIXUP(T, x);
    }
    return y;
}



//注意:x被假想额为多了一层黑色,所有的操作都是为了合理的把这个多出来的
//一重黑色转移到x的父结点

RB-DELETE-FIXUP(T, x)
{
  while (x != root[T] && color[x] == BLACK) {
    if (x == left[p[x]]) { // 如果x为左孩子
        w = right[p[x]];
        if (color[w] == RED) { //case 1: 如果兄弟结点为红色, 则
           color[p[x]] = RED; // 父结点染为红色
           LEFT-ROTATE(T, p[x]);//父结点左旋
           w = right[p[x]];//
        }
        if (color[left[w]]== BLACK && color[right[w]] == BLACK) {//case 2: 如果兄弟结点为黑色,
                                                           //而且兄弟结点的两个孩子都是黑色
          color[w] = RED;
          x = p[x];
        } else {
           if (color[right[w]] == BLACK) {//case 3: 如果兄弟结点为黑色,兄弟结点的右孩子是黑色、
                                                            //左孩子是红色  (-> case 4)
           color[left[w]] = BLACK;
           color[w] = RED;
           RIGHT-ROTATE(T, w);
           w = right[p[x]];
        }
                                             //case 4: 兄弟结点为黑色,兄弟结点的右孩子是红色
        color[w] = color[p[x]];
        color[p[x]] = BLACK;
        color[right[w]] = BLACK;
        LEFT-ROTATE(T, p[x]);
        x = root[T];
    }
  } else { //如果x为右孩子
    same as the clause with "right" and "left" exchanged;
  }
  color[x] = BLACK;
}



Figure 13-7 删除调整操作



- 如果被删除的是红色结点,则红黑树的性质仍然保持:
   + 树中各结点的黑高度都没有变化
   + 不存在两个相邻的红色结点
   + 如果被删除的结点是红色的,就不可能是根,所以根仍然是黑色的


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值