红黑树(Red Black Tree)

红黑树(Red Black Tree)

红黑树(Red Black Tree) 是一种自平衡二叉查找树

红黑树是在1972年由Rudolf Bayer发明的,当时被称为平衡二叉B树(symmetric binary B-trees)后来,在1978年被 Leo J. Guibas 和 Robert Sedgewick 修改为如今的“红黑树”。

红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,但对之进行平衡的代价较低,其平均统计性能要强于 AVL

红黑树的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它查找,插入和删除的时间复杂度为O(logN)

红黑树的性质
1.节点必须是红色或者黑色
2.树根:必为黑色
3.外部节点:均为黑色 (外部节点就是叶子节点为Null,这个是隐含的定义)
4.其余节点:若为红,则只能有黑色孩子。(所以红色节点的父节点一定是黑色)
5.根到外部节点:途中黑节点数目相等,也就是从跟到任何后代 Null 节点都会经过相同数量黑色节点

这些约束会限制路径上不能有两个连续的红色节点
L1 = 最短的路径(可能都是黑色节点)
L2 = 最长的路径(可能全都是交替的红色和黑色节点)

因为性质 5 要求最长的路径都有相同数目的黑色结点
所以没有路径长度大于其他任何路径的 2 倍

下面是一个按顺序添加到 红黑树构建的一个 红黑树结构
数据:10, 6, 16, 4, 12, 8, 18, 3, 2, 1
在这里插入图片描述
从根节点到外部节点经过的黑色节点数为 3

C# 代码实现如下

    /// <summary>
    /// 红黑树
    /// (1)树根:必为黑色
    /// (2)外部节点:均为黑色 (外部节点就是叶子节点为Null,这个是隐含的定义)
    /// (3)其余节点:若为红,则只能有黑色孩子。(所以红色节点的父节点一定是黑色)
    /// (4)根到外部节点:途中黑节点数目相等,也就是从跟到任何后代 Null 节点都会经过相同数量的黑色节点
    /// </summary>
    public class RedBlackTree<T> : BSTree<T> where T : IComparable<T>
    {
        public override BinNode<T> Insert(T t)
        {
            BinNode<T> node = Search(t);
            if (null != node)
            {
                return node;
            }
            //创建新节点以 _hot 为父亲
            node = Insert(t, _hot);
            BinNode<T> nodeOld = node;
            node.Color = Color.Red;
            node.Height = -1;

            //双红修正
            SolveDoubleRed(node);

            return nodeOld;
        }

        public override bool Remove(T t)
        {
            BinNode<T> node = Search(t);
            if (null == node)
            {
                return false;
            }

            BinNode<T> r = Remove(node, ref _hot);
            if (null == Root)
            {
                return true;
            }

            // assert: _hot某一孩子刚被删除,且被r所指节点(可能是null)接替。以下检查是否失衡,并做必要调整
            if (null == _hot) //若刚被删除的是根节点,则将其置黑,并更新黑高度
            {
                Root.Color = Color.Black;
                UpdateHeight(Root);
                return true;
            }
            // assert: 以下,原x(现r)必非根,_hot必非空
            if (BlackHeightUpdated(_hot))
            {
                return true; //若所有祖先的黑深度依然平衡,则无需调整
            }

            if (IsRed(r)) //否则,若r为红,则只需令其转黑
            {
                r.Color = Color.Black;
                r.Height++;
                return true;
            }
            // assert: 以下,原x(现r)均为黑色

            SolveDoubleBlack(r); //经双黑调整后返回
            return true; 
        }

        /// <summary>
        /// 双红修正
        /// </summary>
        protected void SolveDoubleRed(BinNode<T> x)
        {
            if (x.IsRoot()) //若已(递归)转至树根,则将其转黑,整树黑高度也随之递增
            {
                Root.Color = Color.Black;
                Root.Height++; return;
            } //否则,x的父亲p必存在

            BinNode<T> p = x.ParentNode;
            if (IsBlack(p))
            {
                return; //若p为黑,则可终止调整。否则
            }

            BinNode<T> g = p.ParentNode; //既然p为红,则x的祖父必存在,且必为黑色
            BinNode<T> u = Uncle(x); //以下,视x叔父u的颜色分别处理
            if (IsBlack(u))
            { //u为黑色(含null)时 //*DSA*/printf("  case RR-1:\n");
                if (x.IsLChild() == p.IsLChild()) //若x与p同侧(即zIg-zIg或zAg-zAg),则
                {
                    p.Color = Color.Black; //p由红转黑,x保持红
                }
                else //若x与p异侧(即zIg-zAg或zAg-zIg),则
                {
                    x.Color = Color.Black; //x由红转黑,p保持红
                }
                g.Color = Color.Red; //g必定由黑转红
                                   / 以上虽保证总共两次染色,但因增加了判断而得不偿失
                                   / 在旋转后将根置黑、孩子置红,虽需三次染色但效率更高
                BinNode<T> gg = g.ParentNode; //曾祖父(great-grand parent)

                BinNode<T> r = null;//调整后的子树根节点
                if (g.IsRoot())
                {
                    r = Root = RotateAt(x); //调整后的子树根节点
                }
                else if (g.IsLChild())
                {
                    r = g.ParentNode.LeftChild = RotateAt(x); //调整后的子树根节点
                }
                else
                {
                    r = g.ParentNode.RightChild = RotateAt(x); //调整后的子树根节点
                }

                r.ParentNode = gg; //与原曾祖父联接
            }
            else
            { //若u为红色 //*DSA*/printf("  case RR-2:\n");
                p.Color = Color.Black;
                p.Height++; //p由红转黑

                u.Color = Color.Black;
                u.Height++; //u由红转黑
                if (!g.IsRoot())
                {
                    g.Color = Color.Red; //g若非根,则转红
                }
                SolveDoubleRed(g); //继续调整g(类似于尾递归,可优化为迭代形式)
            }
        }

        /// <summary>
        /// 双黑修正
        /// </summary>
        protected void SolveDoubleBlack(BinNode<T> r)
        {
            BinNode<T> p = null != r ? r.ParentNode : _hot;
            if (null == p)
            {
                return; //r的父亲
            }
            BinNode<T> s = (r == p.LeftChild) ? p.RightChild : p.LeftChild; //r的兄弟
            if (IsBlack(s))
            { //兄弟s为黑
                BinNode<T> t = null; //s的红孩子(若左、右孩子皆红,左者优先;皆黑时为null)
                if (null != s && IsRed(s.RightChild))
                {
                    t = s.RightChild; //右子
                }
                if (null != s && IsRed(s.LeftChild))
                {
                    t = s.LeftChild; //左子
                }
                if (null != t)
                { //黑s有红孩子:BB-1
                  //*DSA*/printf("  case BB-1: Child ("); print(s.LeftChild); printf(") of BLACK sibling ("); print(s); printf(") is RED\n");
                    Color oldColor = p.Color; //备份原子树根节点p颜色,并对t及其父亲、祖父
                                              // 以下,通过旋转重平衡,并将新子树的左、右孩子染黑
                    BinNode<T> b = null;
                    if (p.IsRoot())
                    {
                        b = Root = RotateAt(t); //旋转
                    }
                    else if (p.IsLChild())
                    {
                        b = p.ParentNode.LeftChild = RotateAt(t);  //旋转
                    }
                    else
                    {
                        b = p.ParentNode.RightChild = RotateAt(t);  //旋转
                    }

                    //左子
                    if (b.HasLChild())
                    {
                        b.LeftChild.Color = Color.Black;
                        UpdateHeight(b.LeftChild);
                    }
                    //右子
                    if (b.HasRChild())
                    {
                        b.RightChild.Color = Color.Black;
                        UpdateHeight(b.RightChild);
                    } 
                    b.Color = oldColor;
                    UpdateHeight(b); //新子树根节点继承原根节点的颜色
                }
                else //黑s无红孩子
                { 
                    if (null != s)
                    {
                        s.Color = Color.Red;
                        s.Height--; //s转红
                    }
                    
                    if (IsRed(p))//BB-2R
                    { 
                      //*DSA*/printf("  case BB-2R: Both children ("); print(s.LeftChild); printf(") and ("); print(s.RightChild); printf(") of BLACK sibling ("); print(s); printf(") are BLACK, and parent ("); print(p); printf(") is RED\n"); //s孩子均黑,p红
                        p.Color = Color.Black; //p转黑,但黑高度不变
                                             //*DSA*/printBinTree(p, 0, 0);
                    }
                    else
                    { //BB-2B
                      //*DSA*/printf("  case BB-2R: Both children ("); print(s.LeftChild); printf(") and ("); print(s.RightChild); printf(") of BLACK sibling ("); print(s); printf(") are BLACK, and parent ("); print(p); printf(") is BLACK\n"); //s孩子均黑,p黑
                        p.Height--; //p保持黑,但黑高度下降
                                    //*DSA*/printBinTree(p, 0, 0);
                        SolveDoubleBlack(p); //递归上溯
                    }
                }
            }
            else
            { //兄弟s为红:BB-3
              //*DSA*/printf("  case BB-3: sibling ("); print(s); printf(" is RED\n"); //s红(双子俱黑)
                s.Color = Color.Black;
                p.Color = Color.Red; //s转黑,p转红
                BinNode<T> t = s.IsLChild() ? s.LeftChild : s.RightChild; //取t与其父s同侧
                _hot = p;
                if (null != t)
                {
                    if (p.IsRoot())
                    {
                        Root = RotateAt(t); //对t及其父亲、祖父做平衡调整
                    }
                    else if (p.IsLChild())
                    {
                        p.ParentNode.LeftChild = RotateAt(t); //对t及其父亲、祖父做平衡调整
                    }
                    else
                    {
                        p.ParentNode.RightChild = RotateAt(t); //对t及其父亲、祖父做平衡调整
                    }
                }

                SolveDoubleBlack(r); //继续修正r处双黑——此时的p已转红,故后续只能是BB-1或BB-2R
            }
        }

        // 更新节点高度
        protected override int UpdateHeight(BinNode<T> node)
        {
            node.Height = Math.Max(NodeHeight(node.LeftChild), NodeHeight(node.RightChild));
            // 红黑树中各节点左、右孩子的黑高度通常相等
            // 这里之所以取更大值,是便于在删除节点后的平衡调整过程中,正确更新被删除节点父亲的黑高度
            // 否则,rotateAt()会根据被删除节点的替代者(高度小一)设置父节点的黑高度
            if (IsBlack(node)) // 只记黑节点
            {
                node.Height++;
            }
            return node.Height;
        }

        protected bool BlackHeightUpdated(BinNode<T> node)
        {
            int Height = IsRed(node) ? NodeHeight(node.LeftChild) : (NodeHeight(node.LeftChild) + 1);
            if (   (NodeHeight(node.LeftChild) == NodeHeight(node.RightChild))
                && (node.Height == Height)
                )
            {
                return true;
            }

            return false;
        }

        public BinNode<T> Uncle(BinNode<T> node)
        {
            if (node.ParentNode.IsLChild())
            {
                return node.ParentNode.ParentNode.RightChild;
            }
            return node.ParentNode.ParentNode.LeftChild;
        }

        // 外部节点也视作黑节点
        private bool IsBlack(BinNode<T> node)
        {
            return null == node || node.Color == Color.Black;
        }

        // 非黑即红
        private bool IsRed(BinNode<T> node)
        {
            return !IsBlack(node);
        }
    }

红黑树是继承自 二叉搜索树的,上面代码没有 查询函数的实现,因为红黑树的查询跟 二叉搜索树是一样的,所以直接继承子 二叉搜索树,相关代码请到我关于二叉搜索树的篇章寻找

运行效果
在这里插入图片描述
上图中 带 _B 的是黑色节点, 外部节点没有绘制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/548685.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

文件上传App,H5,小程序多端兼容

插件地址&#xff1a;https://ext.dcloud.net.cn/plugin?id5459 下载lsj-upload插件 代码如下 结构 <lsj-upload :option"option" :size"size" :formats"formats" :debug"debug":instantly"instantly" change"…

Element-Ui的Form表单:Label文本两端对齐,且必填项的*不影响布局

1. HTML 结构 首先&#xff0c;确保你的 HTML 或 Vue 模板中有一个 el-form 组件&#xff0c;类似下面这样&#xff1a; <div id"app"><el-form :model"form" label-width"100px"><el-form-item label"用户名">&l…

平台工程在企业数字化转型中的战略价值

要建设成功、有弹性和面向未来的平台&#xff0c;需要做到这三点&#xff1a;了解需求、预测可能面临的挑战并制定经得起时间考验的解决方案。 了解需求是指理解利益相关者的要求和目标&#xff0c;无论他们是最终用户、开发人员还是平台生态系统中的其他相关方。这包括开展全面…

HTTPS 加密解密大致流程

HTTPS简介 在我们开始配置之前&#xff0c;让我们先了解一下HTTPS和它的重要性。 为什么选择HTTPS&#xff1f; 加密传输&#xff1a;通过SSL/TLS协议&#xff0c;确保数据在传输过程中不被窃听。认证身份&#xff1a;确保客户端与预期的服务器通信&#xff0c;防止中间人攻…

梵宁教育:全面解析设计课程,助力职场新人技能飞跃

在数字化浪潮席卷全球的今天&#xff0c;设计行业以其独特的魅力和无穷的创新力&#xff0c;成为职场新人竞相追逐的热门领域。梵宁教育&#xff0c;作为一家专注于设计教育的机构&#xff0c;以其全面而深入的设计课程&#xff0c;为职场新人提供了技能飞跃的有力支持。 梵宁…

vue3 el-table无表头

需要实现的样式 父组件 <template><div><!-- 表格组件 无表头 --><Table :label"tableData.label" :data"tableData.data" :querydata"tableData.querydata" :queryTitle"tableData.title"><template #o…

企业网站建设需要了解什么

在现代商业环境中&#xff0c;企业网站已经成为企业宣传、推广和销售的重要工具。企业网站的建设需要考虑多个因素&#xff0c;包括以下几个方面&#xff1a; 首先&#xff0c;了解企业的目标和定位。企业网站的建设应该围绕企业的目标和定位展开&#xff0c;以达到企业在市场中…

低代码开发平台权威推荐:创新开发、领跑市场!

Gartner是低代码领域的一家权威机构&#xff0c;该机构常常通过"魔力象限"的研究方法&#xff0c;评选全球范围内IT细分领域的产品&#xff0c;来帮助决策者提供重要的咨询建议。本文盘点了Gartner机构推荐的6款低代码平台&#xff1a;Zoho Creator、Mendix、Oracle、…

新零售门店、商品、会员管理指标体系总览

新零售&#xff0c;旨在打破传统零售业的边界&#xff0c;引入先进科技和数字化手段&#xff0c;通过整合线上线下渠道&#xff0c;全面提升用户体验&#xff0c;并实现更智能、高效、个性化的零售运营模式。这一模式不仅仅关注销售产品&#xff0c;更注重构建全方位的购物生态…

(BERT蒸馏)TinyBERT: Distilling BERT for Natural Language Understanding

文章链接&#xff1a;https://arxiv.org/abs/1909.10351 背景 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;预训练语言模型&#xff08;如BERT&#xff09;通过大规模的数据训练&#xff0c;已在多种NLP任务中取得了卓越的性能。尽管BERT模型在语言理解和生成…

【刷题笔记】第一天

两道贪心题 文章目录 [3106. 满足距离约束且字典序最小的字符串](https://leetcode.cn/problems/lexicographically-smallest-string-after-operations-with-constraint/)[3107. 使数组中位数等于 K 的最少操作数](https://leetcode.cn/problems/minimum-operations-to-make-me…

ubuntu安装python3.10

1. 官网下载源程序 2. 解压进入文件夹&#xff1a; ./configure --prefix/usr/local/python3/ 3. 编译安装&#xff1a; make && make install 4. 添加环境变量&#xff1a; vim ~/.bashrc PATH/usr/local/python3/bin:$PATH #保存后&#xff0c;刷新配置文件 sour…

HCIP的学习(8)

OSPF数据报文 OSPF头部信息&#xff08;公共固定&#xff09; 版本&#xff1a;OSPF版本&#xff0c;在IPv4网络中版本字段恒定为数值2&#xff08;v1属于实验室版本&#xff0c;v3属于IPv6&#xff09;类型&#xff1a;代表具体是哪一种报文&#xff0c;按照1~5排序&#xff…

C++从入门到精通——类的6个默认成员函数之赋值运算符重载

赋值运算符重载 前言一、运算符重载定义实例注意要点 二、赋值运算符重载赋值运算符重载格式赋值运算符重载要点重载要点传值返回和传址返回要点 三、前置和后置重载 前言 类的6个默认成员函数&#xff1a;如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真的什么…

xcode c++项目设置运行时参数

在 Xcode 项目中&#xff0c;你可以通过配置 scheme 来指定在运行时传递的参数。以下是在 Xcode 中设置运行时参数的步骤&#xff1a; 打开 Xcode&#xff0c;并打开你的项目。在 Xcode 菜单栏中&#xff0c;选择 "Product" -> "Scheme" -> "E…

利驰软件亮相第二届全国先进技术成果转化大会

4月8日&#xff0c;第二届全国先进技术成果转化大会在苏开幕。省长许昆林出席大会开幕式并致辞。国家国防科工局局长张克俭&#xff0c;省委常委、苏州市委书记刘小涛分别致辞。 本次转化大会由江苏省国防科学技术工业办公室、苏州市人民政府、先进技术成果长三角转化中心主办…

无人棋牌室软硬件方案

先决思考 软件这一套确实是做一套下来&#xff0c;可以无限复制卖出&#xff0c;这个雀氏是一本万利的买卖。 现在肯定是有成套的方案&#xff0c;值不值得重做&#xff1f;为什么要重做&#xff1f; 你想达到什么效果&#xff1f;还是需要细聊的。 做这个东西难度不高&…

自动发版工具以及本地debug

# 定义变量 $jarFile "xxx.jar" $server "ip" $username "user" $password "password" $remoteHost "${username}${server}" $remoteFolderPath "path" $gitDir "$PSScriptRoot\..\.git" # 设置…

每日OJ题_BFS解决最短路①_力扣1926. 迷宫中离入口最近的出口

目录 力扣1926. 迷宫中离入口最近的出口 解析代码 力扣1926. 迷宫中离入口最近的出口 1926. 迷宫中离入口最近的出口 难度 中等 给你一个 m x n 的迷宫矩阵 maze &#xff08;下标从 0 开始&#xff09;&#xff0c;矩阵中有空格子&#xff08;用 . 表示&#xff09;和墙&…

汽车抗疲劳驾驶测试铸铁试验底座技术要求有哪些

铸铁平台试验台底座的主要技术参数要求 1、 试验台底座设计制造符合JB/T794-1999《铸铁平板》标准。 2、 试验铁底板及所有附件的计量单位全部采用 单位&#xff08;SI&#xff09;标准。 3、铸铁平台平板材质&#xff1a;用细密的灰口铸铁HT250或HT200&#xff0c;强度符…