数据结构和算法学习笔记十六:红黑树
一.简介:
红黑树是2-3-4树(一种B树)的实现,所以如果想要理解红黑树的增删操作的原理,必须先了解2-3-4树的增删操作步骤.将红黑树转化为对应的2-3-4树,只需要理解黑色节点才是真正的节点,红色节点是从属于黑色节点的,如下图的红黑树和对应的2-3-4树:
参考资料:一般的做法是将参考资料放在最后的,但是我习惯于将参考资料放在简介中.
1)挑战全B站的红黑树视频,不服来战!终结B站没人能讲清楚红黑树的历史!_哔哩哔哩_bilibili.讲解java中treemap数据结构(底层是红黑树)的实现原理,在讲解的过程中对照2-3-4树详细梳理了红黑树的规则的由来和增删实现,讲解的很细致但是有一定基础的同志会觉得他很啰嗦,适合完全没有基础的人观看.个人觉得后半部分红黑树的删除讲得有些乱.
2)红黑树第一讲_哔哩哔哩_bilibili; 红黑树第二讲_哔哩哔哩_bilibili. 使用C++手撕红黑树,在评论区作者将视频和其中用到的文档课件\源码等放到了度盘中并提供了下载地址,这里也一并粘贴:
地址:https://pan.baidu.com/s/1dCUtkYHRjxzdGGogKv8vaQ
密码:pg9c
这个作者将红黑树的增删过程中遇到的情况分类得很详细,并且将增加节点时双红的解决方案和删除节点时失黑的解决方案梳理得很具体,强烈推荐.本文中部分图片(如上面的2-3-4树和红黑树对应图)也来自于这个作者的文档课件.
二.红黑树的性质
1.红黑树的性质说明:红黑树既然是2-3-4树的实现,那么红黑树的性质就一定是为了使这棵树对应的2-3-4树合乎2-3-4树的规则.因此,要理解红黑树的性质,有必要先理解2-3-4树.2-3-4树是B树的一种,关于B树需要满足的条件及其增删操作的步骤可以参考:数据结构和算法学习笔记十五:多路查找树(B树) - movin2333 - 博客园 (cnblogs.com).在这篇博客中以最简单的2-3树为例讲解了B树的性质和增删操作,2-3-4树的相应增删操作类推即可,如有必要读者也可自行查找资料了解2-3-4树.
2.红黑树的性质:
1)红黑树是一颗平衡二叉树,其中序遍历结果单调不减.
2)节点是红色或者黑色.
3)根节点是黑色.
4)每个叶节点是黑色的.这里的叶节点是指如果某个节点的左右子节点指针中有空值时用来替换这个空值的Null节点.
5)每个红色节点的两个子节点都是黑色节点.换言之,不存在父子节点颜色都是红色的情况.
6)从根节点到每个叶子节点的每一条路径中黑色节点的数量都是相同的.黑色节点的数量叫黑高度.
3.红黑树的性质和2-3-4树的一些关联:
如上图所示,首先2-3-4树本身就是一颗平衡的二叉树,其中序遍历的结果单调不减,因此对应的红黑树也具有相同的性质.其次,在红黑树对应的2-3-4树中,我们发现2-3-4树中的每一种节点都和红黑树中的节点之间有对应关系,这里不详细列举.性质5说不存在两个连续红色节点,也是因为红黑树中两个连续的红色节点的情况在2-3-4树中找不到节点或节点之间的关联情况与之映射.性质6中的黑高度应该很好理解,在2-3-4树中所有叶子节点都在同一层,2-3-4树中每个节点对应到红黑树中都是1个黑色节点和0-2个红色节点.
三.向红黑树中增加元素:
如果向红黑树中增加元素,首先将红黑树当做一颗普通二叉树,使用循环遍历的方法找到要增加节点的位置,然后添加节点.添加的节点默认都是红色的,在添加完成节点后,需要注意有可能遇到连续红色节点的情况,需要进行双红修正.对应到2-3-4树中,添加节点时如果添加到2节点可以直接添加,2节点变为3节点;添加到3节点上,也是直接添加,3节点变为4节点,但是4节点的黑色元素一定是3个元素中中间的那个,所以可能需要修正节点颜色;添加到4节点上时无法直接添加,需要先将原4节点的3个元素中中间的那个挤到父节点中再将剩下两个元素拆成两个2节点,之后再添加节点,而一个元素被挤到父节点中时父节点也可能需要修正.不论2-3-4树中的情况如何,在红黑树中,添加节点时只要遇到了双红现象就需要修正.双红现象又分为以下几种:
1.添加节点时父节点为黑色,没有双红现象发生;
2.叔叔节点是黑色节点或Null节点,这种情况对应到2-3-4树中就是插入到3节点中,只需要进行1-2次旋转和相应的变色即可;
3.叔叔节点是红色,这种情况对应到2-3-4树中就是插入到4节点中,原来的4节点会分裂为两个2节点再添加元素,这时需要递归.
四.移除红黑树中的元素:
红黑树的删除操作相对是比增加操作麻烦的,我们同样先简单梳理2-3-4树中的元素删除:
在2-3-4树中,如果被移除的元素是叶子节点上的元素,且这个叶子节点是3节点或4节点,那么直接移除就好,但是如果这个叶子节点是2节点,则需要找这个节点的后继节点或前驱节点代替这个节点,然后删除前驱节点或后继节点.如果被移除的元素不在叶子节点中,那么同样找到它的前驱节点或后继节点替换它,移除前驱或后继,移除时可以继续找其前驱或后继替换,知道被移除的节点在叶子节点中.
在红黑树中,同样首先找到要移除元素的位置,找到后如果这个节点是一个叶子节点(这里叶子节点不是指Null节点,而是指当前节点的左右子节点指针都为空),直接删除,如果是黑色的节点或不是叶子节点,则找这个节点的前驱节点或后继节点进行替换,替换后继续判断这个节点是否是叶子节点,是就可以删除了.如果叶子节点是红色,直接删除(对应移除2-3-4树中的3节点或4节点),但是叶子节点是黑色的话,就不能直接删除(对应移除2-3-4树中的2节点),删除后会破坏整棵树的平衡,这种情况下需要对移除节点后的红黑树进行重新平衡,这个平衡过程就叫失黑修正.失黑修正的步骤可以分以下几种情况:
1.没有红色侄子,父节点为红色.这种情况的失黑修正只需将父节点变为黑色,兄弟节点变为红色即可.对应2-3-4树中的情况就是在移除2节点时父节点为3节点或4节点,兄弟节点也是2节点,将父节点变为2节点或3节点,多出来的元素移动到兄弟节点中去,这样这个2节点就可以直接移除.
2.有红色侄子,父节点为红色.这种情况的失黑修正只需要在移除后进行一次或两次旋转操作,然后进行染色,染色的结果是父节点位置的新节点为红色,其子节点都为黑色.对应到2-3-4树中,这个操作就是移除2节点时,兄弟节点是3节点或4节点,可以借用父节点中的元素来填补2节点移除后的空缺位置,再从兄弟节点中借用1-2个元素填补父节点被移除后的空缺位置.
3.没有红色侄子,父节点为黑色,兄弟节点为黑色.这种情况的失黑修正无法从兄弟节点找元素补齐,因此会降低红黑树的层数.具体做法是将兄弟节点变为红色,然后向上递归进行失黑修正.
4.父节点为黑色,兄弟节点为红色.这种情况下因为红黑树的不同分支黑高度相同,因此红色的兄弟节点一定有两个黑色的子节点,这时进行一次旋转,就可以将这种情况转化为其他情况.
五.红黑树的增删实现代码(C#):
/************************************ * 创建人:movin * 创建时间:2021/7/28 21:09:48 * 版权所有:个人 ***********************************/ using System; using System.Collections.Generic; using System.Text; namespace SearchCore { /// <summary> /// 红黑树节点类 /// </summary> public class RedBlackNode { /// <summary> /// 保存的数据 /// </summary> public int Content { get; set; } /// <summary> /// 左子节点 /// </summary> public RedBlackNode LeftChild { get; private set; } /// <summary> /// 右子节点 /// </summary> public RedBlackNode RightChild { get; private set; } /// <summary> /// 颜色 /// </summary> public Color Color { get; private set; } /// <summary> /// 父节点 /// </summary> public RedBlackNode Parent { get; private set; } public RedBlackNode(int content,RedBlackNode parent) { Content = content; Parent = parent; //初始节点都是红色 Color = Color.RED; } /// <summary> /// 添加左子节点 /// </summary> /// <param name="leftChild"></param> public void AddLeftChild(RedBlackNode leftChild) { LeftChild = leftChild; if(leftChild != null) { leftChild.Parent = this; } } /// <summary> /// 添加右子节点 /// </summary> /// <param name="rightChild"></param> public void AddRightChild(RedBlackNode rightChild) { RightChild = rightChild; if(rightChild != null) { rightChild.Parent = this; } } /// <summary> /// 设置颜色 /// </summary> /// <param name="color"></param> public void SetColor(Color color) { Color = color; } } /// <summary> /// 红黑树的节点颜色 /// </summary> public enum Color { RED, BLACK, } }
/************************************ * 创建人:movin * 创建时间:2021/7/28 21:10:19 * 版权所有:个人 ***********************************/ using System; using System.Collections.Generic; using System.Text; namespace SearchCore { public class RedBlackTree { /// <summary> /// 根节点 /// </summary> public RedBlackNode Root { get; private set; } /// <summary> /// 节点数量 /// </summary> public int Count { get; private set; } public RedBlackTree() { Count = 0; } #region add and remove node public void Remove(int content) { if(Root == null) { return; } RedBlackNode removeNode = RealRemove(content, Root); if(removeNode != null) { //实际的节点移除操作 if (removeNode.Parent == null) { Root = null; } else { if (removeNode.Parent.LeftChild == removeNode) { removeNode.Parent.AddLeftChild(null); } else { removeNode.Parent.AddRightChild(null); } } //节点数量减少 Count--; } } /// <summary> /// 真正移除节点的方法,递归 /// </summary> /// <param name="content"></param> /// <param name="node"></param> private RedBlackNode RealRemove(int content,RedBlackNode node) { if(content < node.Content) { if(node.LeftChild != null) { return RealRemove(content, node.LeftChild); } } else if(content > node.Content) { if(node.RightChild != null) { return RealRemove(content, node.RightChild); } } //要删除的值和当前节点的值相等时,当前节点就是要删除的节点 else { //找到真正要删除的节点(找当前节点的前驱节点或后继节点,并在找到后就交换数据,并递归或循环继续找,直到找到某个叶子节点身上) var realDeleteNode = GetRealDeleteNode(node); //找到的叶子节点是黑色节点,需要进行失黑修正,再移除;是红色节点,不用失黑修正,直接移除 if(realDeleteNode.Color == Color.BLACK) { LoseBlackAdjust(realDeleteNode); } //返回被移除的节点 return realDeleteNode; } return null; } /// <summary> /// 添加节点 /// </summary> /// <param name="content"></param> public void Add(int content) { RedBlackNode newNode = null; if (Root == null) { newNode = new RedBlackNode(content, null); Root = newNode; } else { newNode = RealAddNode(content, Root); } //如果成功插入了节点,需要作双红修正 if(newNode != null) { RedRedAdjust(newNode); Count++; } } /// <summary> /// 真正添加节点的方法,会递归 /// </summary> /// <param name="content"></param> /// <param name="node"></param> private RedBlackNode RealAddNode(int content, RedBlackNode node) { if (content < node.Content) { if (node.LeftChild == null) { node.AddLeftChild(new RedBlackNode(content, node)); return node.LeftChild; } return RealAddNode(content, node.LeftChild); } else if (content > node.Content) { if (node.RightChild == null) { node.AddRightChild(new RedBlackNode(content, node)); return node.RightChild; } return RealAddNode(content, node.RightChild); } else { return null; } } #endregion #region lose black adjust #region lose black adjust /// <summary> /// 失黑修正 /// </summary> /// <param name="deleteNode"></param> private void LoseBlackAdjust(RedBlackNode deleteNode) { if(deleteNode.Parent != null) { if(deleteNode.Parent.RightChild == deleteNode) { LoseBlackAdjustRight(deleteNode.Parent); } else { LoseBlackAdjustLeft(deleteNode.Parent); } } else { //没有父节点的节点是根节点,根节点必须是黑色节点 deleteNode.SetColor(Color.BLACK); } } /// <summary> /// 失黑修正-移除左子黑色节点 /// </summary> /// <param name="root"></param> private void LoseBlackAdjustLeft(RedBlackNode root) { if(root.Color == Color.BLACK) { if(root.RightChild.Color == Color.RED) { LoseBlackAdjust_BlackParent_RedBrother_Left(root); } else { //兄弟节点为黑色,如果有侄子,侄子节点一定是红色 if(root.RightChild.LeftChild != null || root.RightChild.RightChild != null) { LoseBlackAdjust_WithRedNephew_Left(root); } else { LoseBlackAdjust_BlackParent_BlackBrother_WithoutRedNephew_Left(root); } } } else { if(root.RightChild.LeftChild != null || root.RightChild.RightChild != null) { LoseBlackAdjust_WithRedNephew_Left(root); } else { LoseBlackAdjust_RedParent_BlackBrother_WithoutRedNephew_Left(root); } } } /// <summary> /// 失黑修正-移除右子黑色节点 /// </summary> /// <param name="root"></param> private void LoseBlackAdjustRight(RedBlackNode root) { if (root.Color == Color.BLACK) { if (root.LeftChild.Color == Color.RED) { LoseBlackAdjust_BlackParent_RedBrother_Right(root); } else { //兄弟节点为黑色,如果有侄子,侄子节点一定是红色 if (root.LeftChild.LeftChild != null || root.LeftChild.RightChild != null) { LoseBlackAdjust_WithRedNephew_Right(root); } else { LoseBlackAdjust_BlackParent_BlackBrother_WithoutRedNephew_Right(root); } } } else { if (root.LeftChild.LeftChild != null || root.LeftChild.RightChild != null) { LoseBlackAdjust_WithRedNephew_Right(root); } else { LoseBlackAdjust_RedParent_BlackBrother_WithoutRedNephew_Right(root); } } } #endregion #region with red nephew /// <summary> /// 失黑修正-有红色侄子-移除的黑色节点在根节点左边 /// </summary> /// <param name="root"></param> private void LoseBlackAdjust_WithRedNephew_Left(RedBlackNode root) { //如果没有右子节点的右子节点,要先触发一次右旋 if(root.RightChild.RightChild == null) { RightRotate(root.RightChild); root.RightChild.SetColor(Color.BLACK); root.RightChild.RightChild.SetColor(Color.RED); } //触发一次左旋 LeftRotate(root); //旋转后变色(注意:旋转后root已经不在根节点的位置上了) Color parentColor = root.Color;//保存原来的根节点颜色 root.SetColor(Color.BLACK); root.Parent.SetColor(parentColor);//根节点颜色不变 root.Parent.RightChild.SetColor(Color.BLACK); } /// <summary> /// 失黑修正-有红色侄子-移除的黑色节点在根节点右边 /// </summary> /// <param name="root"></param> private void LoseBlackAdjust_WithRedNephew_Right(RedBlackNode root) { //如果没有左子节点的左子节点,要先触发一次右旋 if (root.LeftChild.LeftChild == null) { LeftRotate(root.LeftChild); root.LeftChild.SetColor(Color.BLACK); root.LeftChild.LeftChild.SetColor(Color.RED); } //触发一次右旋 RightRotate(root); //旋转后变色(注意:旋转后root已经不在根节点的位置上了) Color parentColor = root.Color;//保存原来的根节点颜色 root.SetColor(Color.BLACK); root.Parent.SetColor(parentColor);//根节点颜色不变 root.Parent.LeftChild.SetColor(Color.BLACK); } #endregion #region without red nephew /// <summary> /// 失黑修正-黑色父节点-黑色兄弟节点-没有红色侄子-移除的黑色节点在根节点左边 /// </summary> /// <param name="root"></param> private void LoseBlackAdjust_BlackParent_BlackBrother_WithoutRedNephew_Left(RedBlackNode root) { root.RightChild.SetColor(Color.RED); LoseBlackAdjust(root); } /// <summary> /// 失黑修正-黑色父节点-黑色兄弟节点-没有红色侄子-移除的黑色节点在根节点右边 /// </summary> /// <param name="root"></param> private void LoseBlackAdjust_BlackParent_BlackBrother_WithoutRedNephew_Right(RedBlackNode root) { root.LeftChild.SetColor(Color.RED); LoseBlackAdjust(root); } /// <summary> /// 失黑修正-红色父节点-黑色兄弟节点-没有红色侄子-移除的黑色节点在根节点左边 /// </summary> /// <param name="root"></param> private void LoseBlackAdjust_RedParent_BlackBrother_WithoutRedNephew_Left(RedBlackNode root) { root.SetColor(Color.BLACK); root.RightChild.SetColor(Color.RED); } /// <summary> /// 失黑修正-红色父节点-黑色兄弟节点-没有红色侄子-移除的黑色节点在根节点右边 /// </summary> /// <param name="root"></param> private void LoseBlackAdjust_RedParent_BlackBrother_WithoutRedNephew_Right(RedBlackNode root) { root.SetColor(Color.BLACK); root.LeftChild.SetColor(Color.RED); } /// <summary> /// 失黑修正-黑色父节点-红色兄弟节点-移除的黑色节点在根节点左边 /// </summary> /// <param name="root"></param> private void LoseBlackAdjust_BlackParent_RedBrother_Left(RedBlackNode root) { if(root.RightChild.RightChild == null) { RightRotate(root.RightChild); root.RightChild.RightChild.SetColor(Color.BLACK); } else { root.RightChild.SetColor(Color.BLACK); } LeftRotate(root); root.SetColor(Color.RED); //旋转后还要继续修正 LoseBlackAdjustLeft(root); } /// <summary> /// 失黑修正-黑色父节点-红色兄弟节点-移除的黑色节点在根节点右边 /// </summary> /// <param name="root"></param> private void LoseBlackAdjust_BlackParent_RedBrother_Right(RedBlackNode root) { if (root.LeftChild.LeftChild == null) { LeftRotate(root.LeftChild); root.LeftChild.LeftChild.SetColor(Color.BLACK); } else { root.LeftChild.SetColor(Color.BLACK); } RightRotate(root); root.SetColor(Color.RED); //旋转后还要继续修正 LoseBlackAdjustLeft(root); } #endregion #endregion #region red red adjust #region red red adjust /// <summary> /// 双红在根节点左边时判断进行哪种修正,并进行双红修正 /// </summary> /// <param name="root"></param> private void RedRedAdjust_Left(RedBlackNode root) { //校验不是双红直接返回 if (root.LeftChild.Color == Color.BLACK) { return; } //右节点为空或者右子节点颜色为黑,对应2-3-4树中的3节点 if (root.RightChild == null || root.RightChild.Color == Color.BLACK) { if (root.LeftChild.LeftChild != null && root.LeftChild.LeftChild.Color == Color.RED) { RedRedAdjust_UncleBlack_LeftLeft(root); } else { RedRedAdjust_UncleBlack_LeftRight(root); } } //右子节点颜色为红色,对应2-3-4树中的4节点 else { RedRedAdjust_UncleRed_Left(root); } } /// <summary> /// 双红在根节点右边时判断进行哪种修正,并进行双红修正 /// </summary> /// <param name="root"></param> private void RedRedAdjust_Right(RedBlackNode root) { //校验不是双红直接返回 if (root.RightChild.Color == Color.BLACK) { return; } //右节点为空或者右子节点颜色为黑,对应2-3-4树中的3节点 if (root.LeftChild == null || root.LeftChild.Color == Color.BLACK) { if (root.RightChild.RightChild != null && root.RightChild.RightChild.Color == Color.RED) { RedRedAdjust_UncleBlack_RightRight(root); } else { RedRedAdjust_UncleBlack_RightLeft(root); } } //右子节点颜色为红色,对应2-3-4树中的4节点 else { RedRedAdjust_UncleRed_Right(root); } } /// <summary> /// 新插入节点时进行双红修正 /// </summary> /// <param name="newNode"></param> private void RedRedAdjust(RedBlackNode newNode) { //校验父节点和爷爷节点是否存在,父节点不存在,则这是第一个根节点,爷爷节点不存在则这是第2个或可能是第3个节点 if (newNode.Parent == null) { //根节点必须为黑色 newNode.SetColor(Color.BLACK); return; } if (newNode.Parent.Parent == null) { return; } if (newNode.Parent == newNode.Parent.Parent.LeftChild) { RedRedAdjust_Left(newNode.Parent.Parent); } else { RedRedAdjust_Right(newNode.Parent.Parent); } } #endregion #region black uncle /// <summary> /// 叔叔节点为黑色时的双红修正(新插入节点在父节点左侧,在根节点左侧) /// </summary> /// <param name="grandpa">双红调整前的根节点</param> private void RedRedAdjust_UncleBlack_LeftLeft(RedBlackNode grandpa) { RightRotate(grandpa); //调整颜色(注意:右旋后grandpa已经不是根节点了) grandpa.SetColor(Color.RED); grandpa.Parent.SetColor(Color.BLACK); } /// <summary> /// 叔叔节点为黑色时的双红修正(新插入节点在父节点右侧,在根节点左侧) /// </summary> /// <param name="grandpa">双红调整前的根节点</param> private void RedRedAdjust_UncleBlack_LeftRight(RedBlackNode grandpa) { //先左旋转化为左左的情况 LeftRotate(grandpa.LeftChild); //调用左左情况的修正函数 RedRedAdjust_UncleBlack_LeftLeft(grandpa); } /// <summary> /// 叔叔节点为黑色时的双红修正(新插入节点在父节点右侧,在根节点右侧) /// </summary> /// <param name="grandpa">双红调整前的根节点</param> private void RedRedAdjust_UncleBlack_RightRight(RedBlackNode grandpa) { LeftRotate(grandpa); //调整颜色 grandpa.SetColor(Color.RED); grandpa.Parent.SetColor(Color.BLACK); } /// <summary> /// 叔叔节点为黑色时的双红修正(新插入节点在父节点左侧,在根节点右侧) /// </summary> /// <param name="grandpa">双红调整前的根节点</param> private void RedRedAdjust_UncleBlack_RightLeft(RedBlackNode grandpa) { //先右旋转化为右右的情况 RightRotate(grandpa.RightChild); //调用右右的修正函数 RedRedAdjust_UncleBlack_RightRight(grandpa); } #endregion #region red uncle /// <summary> /// 叔叔节点为黑色时的双红修正(新插入节点在根节点的左子树) /// </summary> /// <param name="grandpa">双红调整前的根节点</param> private void RedRedAdjust_UncleRed_Left(RedBlackNode grandpa) { //直接变色(实际上对应到2-3-4树中是元素的上移和节点的分裂) grandpa.SetColor(Color.RED); grandpa.LeftChild.SetColor(Color.BLACK); //检验是否是根节点,如果是根节点需要增加树的深度,并终止递归 if (grandpa.Parent == null) { //将根节点的颜色改为黑色,红色的叔叔节点改为黑色,树就增长了 grandpa.SetColor(Color.BLACK); grandpa.RightChild.SetColor(Color.BLACK); //无论如何,递归到根节点后都不需要再递归 return; } //向上两层节点不存在也就不用递归了(说明父节点是根节点,一定是黑色) if (grandpa.Parent.Parent == null) { grandpa.RightChild.SetColor(Color.BLACK); return; } //继续向上递归(向上两层) if (grandpa.Parent.Parent.RightChild == grandpa.Parent) { RedRedAdjust_Right(grandpa.Parent.Parent); } else { RedRedAdjust_Left(grandpa.Parent.Parent); } grandpa.RightChild.SetColor(Color.BLACK); } /// <summary> /// 叔叔节点为黑色时的双红修正(新插入节点在根节点的右子树) /// </summary> /// <param name="grandpa">双红调整前的根节点</param> private void RedRedAdjust_UncleRed_Right(RedBlackNode grandpa) { //直接变色(实际上对应到2-3-4树中是元素的上移和节点的分裂) grandpa.SetColor(Color.RED); grandpa.RightChild.SetColor(Color.BLACK); //检验是否是根节点,如果是根节点需要增加树的深度,并终止递归 if (grandpa.Parent == null) { //将根节点的颜色改为黑色,红色的叔叔节点改为黑色,树就增长了 grandpa.SetColor(Color.BLACK); grandpa.LeftChild.SetColor(Color.BLACK); //无论如何,递归到根节点后都不需要再递归 return; } //向上两层节点不存在也就不用递归了(说明父节点是根节点,一定是黑色) if (grandpa.Parent.Parent == null) { grandpa.LeftChild.SetColor(Color.BLACK); return; } //继续向上递归(向上两层) if (grandpa.Parent.Parent.RightChild == grandpa.Parent) { RedRedAdjust_Right(grandpa.Parent.Parent); } else { RedRedAdjust_Left(grandpa.Parent.Parent); } grandpa.LeftChild.SetColor(Color.BLACK); } #endregion #endregion #region leftRotate and rightRotate /// <summary> /// 左旋操作,只做节点的位置变化,不作颜色变化 /// </summary> /// <param name="root">左旋前的根节点</param> private void LeftRotate(RedBlackNode root) { if (root.RightChild == null) { return; } var temp = root.RightChild; root.AddRightChild(temp.LeftChild); if (root.Parent != null) { if (root.Parent.RightChild == root) { root.Parent.AddRightChild(temp); } else { root.Parent.AddLeftChild(temp); } } else { Root = temp; temp.SetParent(null); } temp.AddLeftChild(root); } /// <summary> /// 右旋操作,只做节点的位置变化,不作颜色变化 /// </summary> /// <param name="root">右旋前的根节点</param> private void RightRotate(RedBlackNode root) { if (root.LeftChild == null) { return; } var temp = root.LeftChild; root.AddLeftChild(temp.RightChild); if (root.Parent != null) { if (root.Parent.RightChild == root) { root.Parent.AddRightChild(temp); } else { root.Parent.AddLeftChild(temp); } } else { Root = temp; temp.SetParent(null); } temp.AddRightChild(root); } #endregion #region find real delete node /// <summary> /// 找到真正该删除的节点 /// </summary> /// <param name="currentNode"></param> /// <returns></returns> private RedBlackNode GetRealDeleteNode(RedBlackNode currentNode) { RedBlackNode result = currentNode; while (result.LeftChild != null || result.RightChild != null) { RedBlackNode temp; if(result.LeftChild != null) { //找到左邻居 temp = result.RightChild; while (temp.LeftChild != null) { temp = temp.LeftChild; } } else { //找到右邻居 temp = result.LeftChild; while (temp.RightChild != null) { temp = temp.RightChild; } } //交换节点 result.Content = result.Content ^ temp.Content; temp.Content = result.Content ^ temp.Content; result.Content = result.Content ^ temp.Content; //赋值 result = temp; } //result没有子节点时(是叶子节点)返回 return result; } #endregion } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!