热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

红黑树的删除

红黑树的删除共有12种情况,设要删除的节点为Z:1.Z为根且没有孩子直接删除,将根赋空2.Z为根有一个红孩子(由于第4条性质一定为红孩子)将孩子颜色->黑,当做新

红黑树的删除共有12种情况,

设要删除的节点为Z:

1.Z为根  且没有孩子   直接删除,将根赋空

2.Z为根    有一个红孩子(由于第4条性质一定为红孩子) 将孩子颜色->黑,当做新的根,删除节点

3.Z红色   直接删除,判断Z为父亲的左孩子或右孩子 将其赋空  (此节点一定没有孩子,因为2个孩子的已经处理过,一个红孩子(两个红色节点不能相邻),一个黑孩子(到终端节点黑节点数相同))

4.Z为黑色   有一个红孩子   将红孩子->黑,Z的父与Z的子相连,删除Z

5.Z为黑色   没有孩子(由于要删除的是黑节点,整个过程通俗的理解就是想要借一个黑节点;当侄子为红节点则可以变为黑节点,然后借来一个黑节点

  5.1 兄弟为红(初始状态)

    兄是父右,兄->黑,父->红 ,以父为支点左旋   更新兄

      

    

      兄是父左,兄->黑,父->红,以父为支点右旋,更新兄

  5.2兄弟为黑(初始状态或调整状态)

    5.2.1左侄黑,右侄黑

      5.2.1.1父黑:   兄->红,以父亲为当前节点向上调整    ,更新兄 (由于少了一个黑节点,则向上借黑节点)

        初始状态:

             

        调整状态:

             

      5.2.1.2父红:兄->红,父->黑,结束

        初始状态:

          删除Z即可

        调整中:

          

 

     5.2.2左侄子红,右侄子黑

      5.2.2.1兄为父右:左侄子->黑,兄->红,以兄弟为支点右旋  ,更新兄

        初始状态:

          

        调整中:

          

       5.2.2.2兄为父右:兄弟->父亲的颜色,父->黑,左侄->黑,以父亲为节点右旋  结束

    5.2.3右侄红(右侄子红,左侄子黑;或者右侄子红)

      5.2.3.1兄为父右:兄弟->父亲的颜色,父->黑,右侄->黑,以父亲为支点左旋  结束

        初始状态:

         删除Z即可

        调整中:

         

 

       5.2.3.2兄为父左:右侄->黑,兄->红,以兄弟为支点右旋 ,更新兄弟

 

       通过上图,可以看出,5.2.2.1状态的下一个状态是5.2.3.1

                5.2.3.2状态点的下一个状态是5.2.2.2

代码:需要注意的就是:更新兄弟的时候,由于使用的假删除,要特别注意

RBT* Search(RBT* pRBT,int num)
{
    if(pRBT == NULL) return NULL;
    while(pRBT)
    {
        if(pRBT->val == num)
            return pRBT;
        else if(pRBT->val > num)
            pRBT = pRBT->pLeft;
        else
            pRBT = pRBT->pRight;
    }
    return NULL;
}
RBT* GetBrother(RBT* pRBT)
{
    if(pRBT == NULL) return NULL;
    if(pRBT == pRBT->pFather->pLeft)
        return pRBT->pFather->pRight;
    else
        return pRBT->pFather->pLeft;
}
void DelNode(RBT* pRBT,int num)
{
    if(pRBT == NULL) return ;
    RBT* pdel = Search(pRBT,num);//原始要删除的点
    RBT* ptmp = pdel;//真正要删除的点
    if(pdel == NULL) return;
    if(pdel->pLeft && pdel->pRight)
    {
        ptmp = pdel->pLeft;
        while(ptmp->pRight)
        {
            ptmp = ptmp->pRight;
        }
        pdel->val = ptmp->val;
    }
    if(ptmp == rbt)//为根
    {
        //没有孩子
        if(ptmp->pLeft == NULL && ptmp->pRight == NULL)
        {
            free(ptmp);
            ptmp = NULL;
            rbt = NULL;
            return;
        }
        //有一个孩子
        if(ptmp->pLeft != NULL || ptmp->pRight != NULL)
        {
            rbt = ptmp->pLeft ? ptmp->pLeft : ptmp->pRight;
            free(ptmp);
            ptmp = NULL;
            rbt->color = BLACK;
            rbt->pFather = NULL;
            return;
        }
    }


    //红色
    if(ptmp->color == RED)
    {
        if(ptmp->pFather->pLeft == ptmp)
            ptmp->pFather->pLeft = NULL;
        else
            ptmp->pFather->pRight = NULL;
        free(ptmp);
        ptmp = NULL;
        return;
    }
    if(ptmp->color == BLACK)//真正要删除节点为黑
    {
        //有一个红孩子
        if(ptmp->pLeft || ptmp->pRight)
        {
            if(ptmp == ptmp->pFather->pLeft)
            {
                ptmp->pFather->pLeft = ptmp->pLeft ? ptmp->pLeft : ptmp->pRight;
                ptmp->pFather->pLeft->pFather = ptmp->pFather;
                ptmp->pFather->pLeft->color = BLACK;
            }
            else
            {
                ptmp->pFather->pRight = ptmp->pLeft ? ptmp->pLeft : ptmp->pRight;
                ptmp->pFather->pRight->pFather = ptmp->pFather;
                ptmp->pFather->pRight->color = BLACK;
            }
            free(ptmp);
            ptmp = NULL;
            return;
        }
        //没有孩子
        if(ptmp->pLeft == NULL && ptmp->pRight == NULL)
        {
            RBT* pbrother = GetBrother(ptmp);
            RBT* pfather = ptmp->pFather;
       //假删除
if(ptmp == pfather->pLeft) pfather->pLeft = NULL; else pfather->pRight = NULL; pdel = ptmp; while(1) { //兄弟红 if(pbrother != NULL && pbrother->color == RED) { pbrother->color = BLACK; pbrother->pFather->color = RED; if(pbrother == pbrother->pFather->pRight) { pbrother = pbrother->pLeft;//更新兄弟,为左侄子 LeftSpin(&ptmp->pFather); continue; } if(pbrother == pbrother->pFather->pLeft) { pbrother = pbrother->pRight;//更新兄弟为右侄子 RightSpin(&ptmp->pFather); continue; } } //兄弟黑 if(pbrother->color == BLACK) { //两个侄子黑或者没有侄子 if((pbrother->pLeft && pbrother->pLeft->color == BLACK && pbrother->pRight && pbrother->pRight->color == BLACK)|| (pbrother->pLeft == NULL && pbrother->pRight == NULL)) { if(pbrother->pFather->color == RED) { pbrother->pFather->color = BLACK; pbrother->color = RED; break; } if(pbrother->pFather->color == BLACK) { pbrother->color = RED; ptmp = ptmp->pFather; if(ptmp->pFather == NULL) break; pbrother = GetBrother(ptmp); continue; } } //左侄子为红,右侄子为黑 if(pbrother->pLeft && pbrother->pLeft->color == RED && (pbrother->pRight == NULL || pbrother->pRight->color == BLACK) ) { //兄为父右 if(pbrother == pbrother->pFather->pRight) { pbrother->pLeft->color = BLACK; pbrother->color = RED; RightSpin(&pbrother); pbrother = pbrother->pFather;//由于要以兄弟节点右旋,所以旋转完再更新 continue; } //兄为父左 if(pbrother == pbrother->pFather->pLeft) { pbrother->color = pbrother->pFather->color; pbrother->pLeft->color = BLACK; pbrother->pFather->color = BLACK; RightSpin(&ptmp->pFather); break; } } //右侄子为红 if(pbrother->pRight && pbrother->pRight->color == RED) { //兄为父右 if(pbrother == pbrother->pFather->pRight) { pbrother->pRight->color = BLACK; pbrother->color = pbrother->pFather->color; pbrother->pFather->color = BLACK; LeftSpin(&ptmp->pFather); break; } //兄为父左 if(pbrother == pbrother->pFather->pLeft) { pbrother->pRight->color = BLACK; pbrother->color = RED; LeftSpin(&pbrother); pbrother = pbrother->pFather;//兄弟更新 continue; } } } } } } free(pdel); pdel = NULL; }

    这篇博客是有史以来写的最认真的一个了,希望下次看的时候一下就能看懂,也希望能对看到这篇博客的人有一些帮助~


推荐阅读
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 本文详细介绍了GetModuleFileName函数的用法,该函数可以用于获取当前模块所在的路径,方便进行文件操作和读取配置信息。文章通过示例代码和详细的解释,帮助读者理解和使用该函数。同时,还提供了相关的API函数声明和说明。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • Commit1ced2a7433ea8937a1b260ea65d708f32ca7c95eintroduceda+Clonetraitboundtom ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
author-avatar
手机用户2602936475
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有