红黑树(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 的是黑色节点, 外部节点没有绘制

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

海外商城小程序开发分析

【内容摘要】 在海外做商城小程序的可行性、优势与劣势。 我们将从市场需求、技术实现、竞争环境、政策法规等方面进行分析&#xff0c;为有意进军海外市场的企业提供有价值的参考。 【正文】 一、可行性分析 1. 市场需求&#xff1a;随着全球化的加速&#xff0c;海外市场…

喜讯 | “泰迪杯”数据挖掘挑战赛再次进入计算机类竞赛指数榜单

4月15日&#xff0c;第61届中国高等教育博览会在福州召开。在教师教学发展与创新人才培养论坛上&#xff0c;浙江大学何钦铭教授代表《全国普通高校大学生计算机类竞赛指数》专家工作组发布了最新一年的竞赛指数。据悉&#xff0c;今年的竞赛项目清单包含了28项赛事&#xff0c…

中国人工智能产业年会智能交通与自动驾驶专题全景扫描

中国人工智能产业年会&#xff08;CAIIAC&#xff09;是中国人工智能技术发展和应用的重要展示平台&#xff0c;不仅关注创新&#xff0c;还涵盖了市场和监管方面的内容&#xff0c;对于促进人工智能领域的发展起到了重要作用。年会汇集了来自学术界、工业界和政府的专家&#…

Udio——革命性的AI音乐生成软件

Udio是一款革命性的AI音乐生成软件&#xff0c;由前谷歌DeepMind的顶尖AI研究人员和工程师共同创立&#xff0c;得到著名风险投资公司a16z的支持。它旨在为音乐爱好者和专业人士提供一个全新的音乐创作和分享平台。用户可以通过文本提示来生成音乐&#xff0c;支持广泛的音乐风…

java-spring 图灵 02 手写spring

01.idea中创建一个maven管理的空项目 02.模拟创建出spring容器类&#xff0c;这里叫wzpApplicationContext&#xff0c;创建的时候会自动加载配置类的数据&#xff1a; 这里wzpApplicationContext对标的是AnnotationConfigApplicationContext public class wzpApplicationCo…

泰迪智能科技携手韩山师范学院“企业微专业合作办学招生宣讲”圆满结束

为进一步深化校企合作&#xff0c;落实高校应用型人才培养。2024年4月11日&#xff0c;泰迪智能科技携手韩山师范学院开展企业微专业合作办学招生宣讲会在韩山师范学院顺利举行&#xff0c;本次宣讲会旨在与韩山师范学院学子深入讲解数字经济时代下的企业用工需求&#xff0c;着…

升级win11后无线鼠标失灵,win11鼠标用不了

鼠标失灵是常见的设备故障问题&#xff0c;今天带来相关的解决方法&#xff0c;本文主要是针对升级win11后无线鼠标失灵的处理方法。不少小伙伴在使用电脑的过程中&#xff0c;都遇到过鼠标移动缓慢或者动不了的情况&#xff0c;升级到win11系统的小伙伴也不例外。一般刚升级新…

c# 服务创建

服务 创建服务 编写服务 可以对server1.cs重新命名&#xff0c;点击你的server按F7进入代码编辑模式&#xff0c;编写脚本 双击你的server.cs右击空白位置&#xff0c;添加安装程序&#xff0c;此时会生成“serviceInstaller1”及“serviceProcessInstaller1” 后续可以点击P…

Five86-1 靶机实战

Nmap: 1.存活探测&#xff1a; nmap -sn 192.168.20.1/24 2.端口探测&#xff1a; nmap -sS -p- 192.168.20.131 3.服务探测&#xff1a; nmap -sVC -p 22,80,10000 -O --version-all 192.168.20.131 这里发现 10000 端口也是http服务。 服务漏扫&#xff1a; nmap -p 2…

Shell——执行方式详解

一.什么是shell Shell 是一个计算机程序&#xff0c;它提供了用户与操作系统内核之间的交互界面。它接受来自用户或其他程序的命令&#xff0c;并将其转换为操作系统能理解的形式&#xff0c;然后执行这些命令并将结果返回给用户或程序。 Shell 在操作系统中扮演着重要的角色…

Proxmox VE 实现批量增加多网络

前言 实现批量创建多网络&#xff0c;更改主机名称&#xff0c;hosts解析 初始化网卡&#xff0c;主机名称&#xff0c;hosts解析&#xff0c;重启网卡 我的主机六个网卡&#xff0c;使用的有四个网卡&#xff0c;以下一键创建和初始化主机名称我是以硬件的SN号最为主机的名…

[ C++ ] STL---map与set的使用指南

目录 关联式容器 键值对 set简介 set的常用接口 构造函数 set的迭代器 set的容量 修改相关接口 find()函数 insert()函数 erase()函数 clear()函数 count函数 lower_bound()函数 upper_bound()函数 multiset简介 map简介 map的常用接口 构造函数 map的迭代…