红黑树之删除节点

前人栽树,后人乘凉。关于这样红黑树的帖子已经很多。我就不再重复造轮子了。我自己发现一个删除说的很好的博客,链接:http://gengning938.blog.163.com/blog/static/1282253812011420103852696/

我只是说说自己的理解。方便以后自己回来忘了的时候,能很快看懂。


分为几种情况:

1、删除的是叶子节点。那么,如果是红色,直接删除。如果是黑色,则这条分支少了一个黑色节点。需要调整。


2、删除的不是叶子节点,那么从他的右子树中找一个最小的节点和他交换,然后要删除的点就移到了叶子节点,回到情况1。


那么这个时候回到情况1了。这个运气好是红色的。但是如果是删除黑色的点呢?比如删除根节点13 。

如果删除的是13,那么右子树最小的是15,交换13和15。然后删除位置变为15。这个时候要删除的是黑色的节点,那就复杂了。又可以分为下面几种情况

1、删除的点的兄弟是红色,那么他的父亲,孩子都是黑色。这一种,交换兄弟与父亲的颜色,然后以父节点为准,旋转树,旋转之后新的兄弟变成了黑色。。转到下一种情况

2、删除的点的兄弟是黑色。又可以分为几种情况:

1)、黑色的兄弟的孩子都是黑色,那么把兄弟染成红色,这样,兄弟子树和自己都少了一个黑色节点,兄弟子树和自己平衡了。但是整个子树又比其他子树少了一个黑色,所以需要看父亲节点是红色还是黑色,如果父亲是红色,直接染黑就完全平衡了,但是父亲节点如果是黑色,那就继续看父亲的兄弟颜色。。

2)、黑色的兄弟孩子有红色的。那么如果远亲侄子是红色,那么远亲侄子染成黑色,交换父亲与兄弟的颜色,然后以父亲为准,根据删除点是父亲的左孩子还是右孩子进行相应的旋转,如果是左孩子就左旋,如果是右孩子就右旋。最后平衡。

3)、如果黑色的兄弟的远亲侄子是红色,那就转换为2)的情况。


所以复杂的情况就是四种,第1种,和第二种的三种情况。

以下是代码,写的时候参照了一下STL :

  1. void tst_rbt_delete(tst_rbtree* tree, tst_rbtnode* node)  
  2. {  
  3.     tst_rbtnode *root, *subt, *temp, *sentinel, *w;  
  4.     root = &tree->root;  
  5.     sentinel = tree->sentinel;  
  6.       
  7.     /二叉树的删除/  
  8.       
  9.     //找到要删除的节点。  
  10.     if (sentinel == node->lchild)        //fd1  
  11.     {  
  12.         subt = node->rchild;  
  13.         temp = node;  
  14.     }  
  15.     else if (sentinel == node->rchild)   //fd2  
  16.     {  
  17.         subt = node->lchild;  
  18.         temp = node;  
  19.     }  
  20.     else                                //fd3  
  21.     {  
  22.         //node的左右孩子都不为空,那么从右子树中找到key最小的节点,交换node和这个点,再删除node  
  23.         temp = tst_rbt_min_node(node->rchild, sentinel);  
  24.         if (temp->lchild == sentinel)  
  25.         {     
  26.             //既然是最小的节点,其实并不会进入这个分支,如果还有左孩子,肯定不是最小的  
  27.             subt = temp->rchild;  
  28.         }  
  29.         else  
  30.         {  
  31.             subt = temp->lchild;  
  32.         }  
  33.     }  
  34.     //如果要删除的点是根节点,这种情况只会是fd1,fd2中的一种  
  35.     if (temp == *root)  
  36.     {  
  37.         *root = subt;  
  38.         tst_rbt_set_black(subt);  
  39.         tst_rbt_node_reset(node);  
  40.         return;  
  41.     }  
  42.   
  43.     unsigned char isRed = tst_rbt_is_red(temp);  
  44.   
  45.     //把找到的节点移出树外。孩子上提  
  46.     if (tst_rbt_is_lchild(temp))  
  47.     {  
  48.         tst_rbt_parent(temp)->lchild = subt;  
  49.     }  
  50.     else  
  51.     {  
  52.         tst_rbt_parent(temp)->rchild = subt;  
  53.     }  
  54.     if (temp == node)  
  55.     {     
  56.         //如果要删除的点就是node,那么node已经被移出树外  
  57.         subt->parent = temp->parent;  
  58.     }  
  59.     else  
  60.     {  
  61.         //如果要删除的点不是node,而是右子树中最小的节点  
  62.   
  63.         if (node == tst_rbt_parent(temp))  
  64.         {  
  65.             //如果node是temp的父节点,那么,subt的父节点还是temp,但是如果subt是sentinel,那么sentinel一开始parent是NULL  
  66.             subt->parent = temp;  
  67.         }  
  68.         else  
  69.         {  
  70.             //如果不是,那么subt的父节点该是temp的父节点  
  71.             subt->parent = temp->parent;  
  72.         }  
  73.   
  74.         temp->parent = node->parent;  
  75.         temp->lchild = node->lchild;  
  76.         temp->rchild = node->rchild;  
  77.         tst_rbt_copy_color(temp, node);  
  78.   
  79.         if (node == *root)  
  80.         {  
  81.             //如果node是根,设置根为找到的节点  
  82.             *root = temp;  
  83.         }  
  84.         else  
  85.         {  
  86.             //把找到的点替换node  
  87.             if (tst_rbt_is_lchild(node))  
  88.             {  
  89.                 tst_rbt_parent(node)->lchild = temp;  
  90.             }  
  91.             else  
  92.             {  
  93.                 tst_rbt_parent(node)->rchild = temp;  
  94.             }  
  95.         }  
  96.   
  97.         if (!tst_rbt_is_leaf(tree, temp->lchild))  
  98.         {  
  99.             temp->lchild->parent = temp;  
  100.         }  
  101.         if (!tst_rbt_is_leaf(tree, temp->rchild))  
  102.         {  
  103.             temp->rchild->parent = temp;  
  104.         }  
  105.     }  
  106.   
  107.     tst_rbt_node_reset(node);  
  108.       
  109.     if (isRed)  
  110.     {  
  111.         return;  
  112.     }  
  113.   
  114.     //dsptree(*root, sentinel);//打印树  
  115.   
  116.     / 重新平衡红黑树 /  
  117.     while (subt != *root && tst_rbt_is_black(subt))  
  118.     {  
  119.         if (tst_rbt_is_lchild(subt))  
  120.         {  
  121.             w = tst_rbt_parent(subt)->rchild;  
  122.             /复杂情况1: 
  123.             兄弟是红色,那么父节点,兄弟的孩子都是黑色。交换兄弟与父亲的颜色 
  124.             然后左旋。转换情况为要删除的点的兄弟是黑色。 
  125.             /  
  126.             if (tst_rbt_is_red(w))//w是红色。则w的parent,child必然是黑色  
  127.             {  
  128.                 tst_rbt_set_red(tst_rbt_parent(subt));  
  129.                 tst_rbt_set_black(w);  
  130.                 tst_rbt_rotate_left(root, tst_rbt_parent(subt), sentinel);  
  131.                 w = tst_rbt_parent(subt)->rchild;//转化为subt的兄弟是黑色的情况  
  132.             }  
  133.             /复杂情况2: 
  134.             如果要删除的点的兄弟不是红色,并且兄弟的左右孩子都是黑色 
  135.             那么把兄弟染成红色,再看父节点。如果是红色,跳出循环,最后染成黑色,ok 
  136.             如果父节点是黑色,那么又回到最原始的问题(情况1234都可能) 
  137.             /  
  138.             if (tst_rbt_is_black(w->lchild) && tst_rbt_is_black(w->rchild))  
  139.             {  
  140.                 tst_rbt_set_red(w);  
  141.                 subt = tst_rbt_parent(subt);  
  142.             }  
  143.             else  
  144.             {  
  145.                 /复杂情况3:兄弟节点有红色孩子 
  146.                 如果兄弟的远侄子是黑色,那么肯定近亲侄子是红色。 
  147.                 最终目的是把远侄子变成红色,转换成下一种情况,可以最终解决问题。 
  148.                 /  
  149.                 if (tst_rbt_is_black(w->rchild))  
  150.                 {  
  151.                     tst_rbt_set_black(w->lchild);  
  152.                     tst_rbt_set_red(w);  
  153.                     tst_rbt_rotate_right(root, w, sentinel);  
  154.                     w = tst_rbt_parent(subt)->rchild;  
  155.                 }  
  156.   
  157.                 /复杂情况4: 
  158.                 如果远侄子是红色,直接把远侄子设为黑色,交换父亲与兄弟颜色 
  159.                 最终选择合适的旋转方向旋转子树。达到平衡。 
  160.                 /  
  161.                 tst_rbt_copy_color(w, tst_rbt_parent(subt));  
  162.                 tst_rbt_set_black(tst_rbt_parent(subt));  
  163.                 tst_rbt_set_black(w->rchild);  
  164.                 tst_rbt_rotate_left(root, tst_rbt_parent(subt), sentinel);  
  165.                 subt = root;//结束循环,同时方便后续设置根节点为黑色  
  166.             }  
  167.   
  168.         }  
  169.         else  
  170.         {  
  171.             //与上面,删除点是父亲的左孩子相似,只不过,旋转树的时候方向与上面情况相反  
  172.             w = tst_rbt_parent(subt)->lchild;  
  173.             if (tst_rbt_is_red(w))//w是红色。w的parent,child必然是黑色  
  174.             {  
  175.                 tst_rbt_set_black(w);  
  176.                 tst_rbt_set_red(tst_rbt_parent(subt));  
  177.                 tst_rbt_rotate_right(root, tst_rbt_parent(subt), sentinel);  
  178.                 w = tst_rbt_parent(subt)->lchild;  
  179.             }  
  180.             if (tst_rbt_is_black(w->lchild) && tst_rbt_is_black(w->rchild))  
  181.             {  
  182.                 tst_rbt_set_red(w);  
  183.                 subt = tst_rbt_parent(subt);  
  184.             }  
  185.             else  
  186.             {  
  187.                 if (tst_rbt_is_black(w->lchild))  
  188.                 {  
  189.                     tst_rbt_set_red(w);  
  190.                     tst_rbt_set_black(w->rchild);  
  191.                     tst_rbt_rotate_left(root, w, sentinel);  
  192.                     w = tst_rbt_parent(subt)->lchild;  
  193.                 }  
  194.                 tst_rbt_copy_color(w, tst_rbt_parent(subt));  
  195.                 tst_rbt_set_black(tst_rbt_parent(subt));  
  196.                 tst_rbt_set_black(w->lchild);  
  197.                 tst_rbt_rotate_right(root, tst_rbt_parent(subt), sentinel);  
  198.                 subt = *root;  
  199.             }  
  200.   
  201.         }  
  202.         //dsptree(*root, sentinel);//打印树  
  203.     }  
  204.     tst_rbt_set_black(subt);  
  205. }  
void tst_rbt_delete(tst_rbtree tree, tst_rbtnode* node) 
{
tst_rbtnode **root, *subt, *temp, *sentinel, *w;
root = &tree->root;
sentinel = tree->sentinel;

/*二叉树的删除*/

//找到要删除的节点。
if (sentinel == node->lchild)        //fd1
{
    subt = node->rchild;
    temp = node;
}
else if (sentinel == node->rchild)   //fd2
{
    subt = node->lchild;
    temp = node;
}
else                                //fd3
{
    //node的左右孩子都不为空,那么从右子树中找到key最小的节点,交换node和这个点,再删除node
    temp = tst_rbt_min_node(node->rchild, sentinel);
    if (temp->lchild == sentinel)
    {   
        //既然是最小的节点,其实并不会进入这个分支,如果还有左孩子,肯定不是最小的
        subt = temp->rchild;
    }
    else
    {
        subt = temp->lchild;
    }
}
//如果要删除的点是根节点,这种情况只会是fd1,fd2中的一种
if (temp == *root)
{
    *root = subt;
    tst_rbt_set_black(subt);
    tst_rbt_node_reset(node);
    return;
}

unsigned char isRed = tst_rbt_is_red(temp);

//把找到的节点移出树外。孩子上提
if (tst_rbt_is_lchild(temp))
{
    tst_rbt_parent(temp)->lchild = subt;
}
else
{
    tst_rbt_parent(temp)->rchild = subt;
}
if (temp == node)
{   
    //如果要删除的点就是node,那么node已经被移出树外
    subt->parent = temp->parent;
}
else
{
    //如果要删除的点不是node,而是右子树中最小的节点

    if (node == tst_rbt_parent(temp))
    {
        //如果node是temp的父节点,那么,subt的父节点还是temp,但是如果subt是sentinel,那么sentinel一开始parent是NULL
        subt->parent = temp;
    }
    else
    {
        //如果不是,那么subt的父节点该是temp的父节点
        subt->parent = temp->parent;
    }

    temp->parent = node->parent;
    temp->lchild = node->lchild;
    temp->rchild = node->rchild;
    tst_rbt_copy_color(temp, node);

    if (node == *root)
    {
        //如果node是根,设置根为找到的节点
        *root = temp;
    }
    else
    {
        //把找到的点替换node
        if (tst_rbt_is_lchild(node))
        {
            tst_rbt_parent(node)->lchild = temp;
        }
        else
        {
            tst_rbt_parent(node)->rchild = temp;
        }
    }

    if (!tst_rbt_is_leaf(tree, temp->lchild))
    {
        temp->lchild->parent = temp;
    }
    if (!tst_rbt_is_leaf(tree, temp->rchild))
    {
        temp->rchild->parent = temp;
    }
}

tst_rbt_node_reset(node);

if (isRed)
{
    return;
}

//dsptree(*root, sentinel);//打印树

/* 重新平衡红黑树 */
while (subt != *root && tst_rbt_is_black(subt))
{
    if (tst_rbt_is_lchild(subt))
    {
        w = tst_rbt_parent(subt)->rchild;
        /*复杂情况1:
        *兄弟是红色,那么父节点,兄弟的孩子都是黑色。交换兄弟与父亲的颜色
        *然后左旋。转换情况为要删除的点的兄弟是黑色。
        */
        if (tst_rbt_is_red(w))//w是红色。则w的parent,child必然是黑色
        {
            tst_rbt_set_red(tst_rbt_parent(subt));
            tst_rbt_set_black(w);
            tst_rbt_rotate_left(root, tst_rbt_parent(subt), sentinel);
            w = tst_rbt_parent(subt)->rchild;//转化为subt的兄弟是黑色的情况
        }
        /*复杂情况2:
        *如果要删除的点的兄弟不是红色,并且兄弟的左右孩子都是黑色
        *那么把兄弟染成红色,再看父节点。如果是红色,跳出循环,最后染成黑色,ok
        *如果父节点是黑色,那么又回到最原始的问题(情况1234都可能)
        */
        if (tst_rbt_is_black(w->lchild) && tst_rbt_is_black(w->rchild))
        {
            tst_rbt_set_red(w);
            subt = tst_rbt_parent(subt);
        }
        else
        {
            /*复杂情况3:兄弟节点有红色孩子
            *如果兄弟的远侄子是黑色,那么肯定近亲侄子是红色。
            *最终目的是把远侄子变成红色,转换成下一种情况,可以最终解决问题。
            */
            if (tst_rbt_is_black(w->rchild))
            {
                tst_rbt_set_black(w->lchild);
                tst_rbt_set_red(w);
                tst_rbt_rotate_right(root, w, sentinel);
                w = tst_rbt_parent(subt)->rchild;
            }

            /*复杂情况4:
            *如果远侄子是红色,直接把远侄子设为黑色,交换父亲与兄弟颜色
            *最终选择合适的旋转方向旋转子树。达到平衡。
            */
            tst_rbt_copy_color(w, tst_rbt_parent(subt));
            tst_rbt_set_black(tst_rbt_parent(subt));
            tst_rbt_set_black(w->rchild);
            tst_rbt_rotate_left(root, tst_rbt_parent(subt), sentinel);
            subt = *root;//结束循环,同时方便后续设置根节点为黑色
        }

    }
    else
    {
        //与上面,删除点是父亲的左孩子相似,只不过,旋转树的时候方向与上面情况相反
        w = tst_rbt_parent(subt)->lchild;
        if (tst_rbt_is_red(w))//w是红色。w的parent,child必然是黑色
        {
            tst_rbt_set_black(w);
            tst_rbt_set_red(tst_rbt_parent(subt));
            tst_rbt_rotate_right(root, tst_rbt_parent(subt), sentinel);
            w = tst_rbt_parent(subt)->lchild;
        }
        if (tst_rbt_is_black(w->lchild) && tst_rbt_is_black(w->rchild))
        {
            tst_rbt_set_red(w);
            subt = tst_rbt_parent(subt);
        }
        else
        {
            if (tst_rbt_is_black(w->lchild))
            {
                tst_rbt_set_red(w);
                tst_rbt_set_black(w->rchild);
                tst_rbt_rotate_left(root, w, sentinel);
                w = tst_rbt_parent(subt)->lchild;
            }
            tst_rbt_copy_color(w, tst_rbt_parent(subt));
            tst_rbt_set_black(tst_rbt_parent(subt));
            tst_rbt_set_black(w->lchild);
            tst_rbt_rotate_right(root, tst_rbt_parent(subt), sentinel);
            subt = *root;
        }

    }
    //dsptree(*root, sentinel);//打印树
}
tst_rbt_set_black(subt);

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值