【C++】模拟实现红黑树(插入)

目录

红黑树的概念

红黑树的性质

红黑树的调整情况

红黑树的模拟实现

枚举类型的定义

红黑树节点的定义

插入函数的实现

旋转函数的实现

左旋

右旋

自检函数的实现

红黑树类


红黑树的概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的

红黑树的性质

  1. 每个结点不是红色就是黑色

  2. 根节点是黑色的

  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的

  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点

  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

红黑树的调整情况

  • 新节点插入为红色:

新节点默认着色为红色。

  • 父节点为黑色:

如果新插入节点的父节点是黑色,那么通常只需要进行简单的颜色调整即可,因为黑色节点不会破坏红黑树的性质。

  • 父节点为红色:

这是需要重点关注的情况,因为红色父节点可能违反红黑树的性质。

  • 叔节点也为红色: 如果叔节点(父节点的兄弟节点)也是红色,则将父节点和叔节点都变为黑色,并将祖父节点(父节点的父节点)变为红色。接着,将祖父节点作为新的当前节点,继续向上检查是否破坏了性质。 -叔节点为黑色或不存在: 根据新节点是父节点的左子节点还是右子节点,以及父节点是祖父节点的左子节点还是右子节点,需要进行不同的调整。

  • 父节点是左子节点:

新节点是父节点的右子节点:将父节点进行左旋操作,然后重新从父节点开始着色。 新节点是父节点的左子节点:将父节点变为黑色,将祖父节点变为红色,然后对祖父节点进行右旋操作。

  • 父节点是右子节点:

新节点是父节点的左子节点:将父节点进行右旋操作,然后重新从父节点开始着色。

红黑树的模拟实现

枚举类型的定义

enum Color{RED,BLACK};

红黑树节点的定义

  • 成员有:左孩子指针、有孩子指针、父节点指针、一个键值对、颜色(枚举类型)

  • 构造函数:将三个指针置空,颜色默认为红色,由外面传递的键值对作为值

struct RBTreeNode{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;pair<K, V> _kv;Colour _color;RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _color(RED){}};

插入函数的实现

  • 函数参数:

const pair<K, V>& kv:要插入到红黑树中的键值对。

  • 检查根节点是否为空:

if (_root == nullptr):如果红黑树为空(即根节点为空),则创建一个新的节点作为根节点,并设置其颜色为黑色。然后返回true表示插入成功。

  • 查找插入位置:

使用parent和cur指针遍历树,查找插入位置。parent用于记录当前节点的父节点,cur用于遍历树。根据键值对的大小关系,将cur指针移动到正确的子树中。如果在遍历过程中发现键值对已经存在于树中(即cur->_kv.first == kv.first),则直接返回false表示插入失败。

  • 创建新节点并插入:

创建一个新的节点cur,并设置其值为kv,颜色为红色。根据之前找到的parent节点的键值对大小关系,将新节点插入到父节点的左子树或右子树中,并更新节点的父节点指针。

  • 调整红黑树:

由于新插入的节点颜色是红色,可能会破坏红黑树的性质。因此,需要进行一系列调整操作来恢复红黑树的性质。while (parent && parent->_color == RED)循环用于检查父节点的颜色,如果父节点是红色的,则需要进行调整。 根据父节点是祖父节点的左子节点还是右子节点,以及新节点是父节点的左子节点还是右子节点,选择不同的调整策略。如果父节点和叔叔节点(祖父节点的另一个子节点)都是红色的,则将父节点和叔叔节点的颜色改为黑色,并将祖父节点的颜色改为红色。然后更新cur和parent指针,继续检查新的父节点。 如果叔叔节点是黑色的,则根据新节点是父节点的左子节点还是右子节点,选择进行左旋或右旋操作,以及可能的颜色调整。在循环结束后,确保根节点的颜色是黑色,这是红黑树的一个基本性质。

  • 返回插入结果:

由于插入操作已经成功完成,无论是否进行了调整操作,都返回true表示插入成功。

bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);_root->_color = BLACK;return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);cur->_color = RED;if (parent->_kv.first < kv.first){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}while (parent && parent->_color == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;if (uncle && uncle->_color == RED){parent->_color = uncle->_color = BLACK;grandfather->_color = RED;cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_left){RotateR(grandfather);parent->_color = BLACK;grandfather->_color = RED;}else{RotateL(parent);RotateL(grandfather);cur->_color = BLACK;grandfather->_color = RED;}break;}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_color == RED){parent->_color = uncle->_color = BLACK;grandfather->_color = RED;cur = grandfather;}else{if (cur == parent->_right){RotateL(grandfather);parent->_color = BLACK;grandfather->_color = RED;}else{RotateR(parent);RotateL(grandfather);cur->_color = BLACK;grandfather->_color = RED;}break;}}}_root->_color = BLACK;return true;}

旋转函数的实现

  • 这两个函数RotateL和RotateR分别用于执行红黑树的左旋和右旋操作。在红黑树的插入和删除操作中,当破坏了红黑树的性质时,通常需要通过节点的旋转和颜色调整来恢复树的平衡。

左旋

  • 左旋操作通常用于当某个节点的右子节点是红色时。左旋操作将右子节点提升为父节点,并将原父节点降为其左子节点。同时,原父节点的左子节点则成为新父节点的右子节点。

void RotateL(Node* parent)  
{  Node* subR = parent->_right; // 右子节点  Node* subRL = subR->_left; // 右子节点的左子节点  parent->_right = subRL; // 原父节点的右子节点指向右子节点的左子节点  if (subRL)  subRL->_parent = parent; // 更新subRL的父节点指针  subR->_left = parent; // 右子节点的左子节点指向原父节点  parent->_parent = subR; // 更新原父节点的父节点指针  Node* parentParent = parent->_parent; // 原父节点的父节点  if (_root == parent) // 如果原父节点是根节点  {  _root = subR; // 更新根节点为右子节点  subR->_parent = nullptr; // 根节点的父节点指针置为null  }  else  {  if (parentParent->_left == parent) // 如果原父节点是其父节点的左子节点  {  parentParent->_left = subR; // 更新父节点的左子节点为右子节点  }  else  {  parentParent->_right = subR; // 如果原父节点是其父节点的右子节点,则更新父节点的右子节点为右子节点  }  subR->_parent = parentParent; // 更新右子节点的父节点指针  }  
}

右旋

  • 右旋操作与左旋操作相反,通常用于当某个节点的左子节点是红色时。右旋操作将左子节点提升为父节点,并将原父节点降为其右子节点。同时,原父节点的右子节点则成为新父节点的左子节点。

  void RotateR(Node* parent)  
{  Node* subL = parent->_left; // 左子节点  Node* subLR = subL->_right; // 左子节点的右子节点  parent->_left = subLR; // 原父节点的左子节点指向左子节点的右子节点  if (subLR)  subLR->_parent = parent; // 更新subLR的父节点指针  subL->_right = parent; // 左子节点的右子节点指向原父节点  parent->_parent = subL; // 更新原父节点的父节点指针  Node* parentParent = parent->_parent; // 原父节点的父节点  if (_root == parent) // 如果原父节点是根节点  {  _root = subL; // 更新根节点为左子节点  subL->_parent = nullptr; // 根节点的父节点指针置为null  }  else  {  if (parentParent->_left == parent) // 如果原父节点是其父节点的左子节点  {  parentParent->_left = subL; // 更新父节点的左子节点为左子节点  }  else  {  parentParent->_right = subL; // 如果原父节点是其父节点的右子节点,则更新父节点的右子节点为左子节点  }  subL->_parent = parentParent; // 更新左子节点的父节点指针  }  
}

自检函数的实现

  • Check函数

这个函数递归地检查红黑树的每个节点,确保满足红黑树的性质。

首先,它检查传入的节点是否为空(即已经到达了叶子节点)。如果是,它会比较从根节点到该叶子节点的黑色节点数量是否等于refVal(预期值)。如果不等,则打印错误信息并返回false。接着,它检查当前节点是否与其父节点连续为红色,如果是,则打印错误信息并返回false。如果当前节点是黑色,则增加blacknum的计数。最后,它递归地检查左子树和右子树。

  • IsBalance 函数

这个函数用于检查整棵红黑树是否平衡。

首先,它检查根节点是否为空或红色。如果根节点为空,则树是平衡的;如果根节点为红色,则树不平衡。接下来,它计算从根节点到最左侧叶子节点路径上的黑色节点数量,并将此值作为refVal(预期值)。然后,它调用Check函数,从根节点开始,检查整棵树是否满足红黑树的性质,特别是黑色节点数量的要求。

bool Check(Node* root, int blacknum, const int refVal){if (root == nullptr){//cout << balcknum << endl;if (blacknum != refVal){cout << "存在黑色节点数量不相等的路径" << endl;return false;}return true;}if (root->_color == RED && root->_parent->_color == RED){cout << "有连续的红色节点" << endl;return false;}if (root->_color == BLACK){++blacknum;}return Check(root->_left, blacknum, refVal)&& Check(root->_right, blacknum, refVal);}public:bool IsBalance(){if (_root == nullptr)return true;if (_root->_color == RED)return false;//参考值int refVal = 0;Node* cur = _root;while (cur){if (cur->_color == BLACK){++refVal;}cur = cur->_left;}int blacknum = 0;return Check(_root, blacknum, refVal);}

红黑树类

  • 使用KV模型

  • 私有成员为红黑树根节点指针

  • 包含插入函数、旋转函数、中序遍历、判断是否是红黑树等

template<class K, class V>class RBTree{typedef RBTreeNode<K, V> Node;public:bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);_root->_color = BLACK;return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);cur->_color = RED;if (parent->_kv.first < kv.first){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}while (parent && parent->_color == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;if (uncle && uncle->_color == RED){parent->_color = uncle->_color = BLACK;grandfather->_color = RED;cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_left){RotateR(grandfather);parent->_color = BLACK;grandfather->_color = RED;}else{RotateL(parent);RotateL(grandfather);cur->_color = BLACK;grandfather->_color = RED;}break;}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_color == RED){parent->_color = uncle->_color = BLACK;grandfather->_color = RED;cur = grandfather;}else{if (cur == parent->_right){RotateL(grandfather);parent->_color = BLACK;grandfather->_color = RED;}else{RotateR(parent);RotateL(grandfather);cur->_color = BLACK;grandfather->_color = RED;}break;}}}_root->_color = BLACK;return true;}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;subR->_left = parent;Node* parentParent = parent->_parent;parent->_parent = subR;if (subRL)subRL->_parent = parent;if (_root == parent){_root = subR;subR->_parent = nullptr;}else{if (parentParent->_left == parent){parentParent->_left = subR;}else{parentParent->_right = subR;}subR->_parent = parentParent;}}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* parentParent = parent->_parent;subL->_right = parent;parent->_parent = subL;if (_root == parent){_root = subL;subL->_parent = nullptr;}else{if (parentParent->_left == parent){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}}void InOrder(){_InOrder(_root);cout << endl;}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << " ";_InOrder(root->_right);}private:// 根节点->当前节点这条路径的黑色节点的数量bool Check(Node* root, int blacknum, const int refVal){if (root == nullptr){//cout << balcknum << endl;if (blacknum != refVal){cout << "存在黑色节点数量不相等的路径" << endl;return false;}return true;}if (root->_color == RED && root->_parent->_color == RED){cout << "有连续的红色节点" << endl;return false;}if (root->_color == BLACK){++blacknum;}return Check(root->_left, blacknum, refVal)&& Check(root->_right, blacknum, refVal);}public:bool IsBalance(){if (_root == nullptr)return true;if (_root->_color == RED)return false;//参考值int refVal = 0;Node* cur = _root;while (cur){if (cur->_color == BLACK){++refVal;}cur = cur->_left;}int blacknum = 0;return Check(_root, blacknum, refVal);}private:Node* _root = nullptr;};
}

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

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

相关文章

【剑指offr--C/C++】JZ7 重建二叉树

一、题目 二、思路及代码 前序遍历&#xff1a;中、左、右。所以前序遍历的第一个节点是树的根节点&#xff0c;第二个节点是左子树的根节点。。。。 中序遍历&#xff1a;左、中、右。树的根节点在中间某处 我们可以根据二者的特点结合一下&#xff1a;对于前序遍历序列{1,2,4…

查看MySQL版本的方式

文章目录 一、使用cmd输入命令行查看二、在mysql客户端服务器里查询 一、使用cmd输入命令行查看 1、打开 cmd &#xff0c;输入命令行&#xff1a; mysql --version 2、还是打开cmd&#xff0c;输入命令行&#xff1a;mysql -V (注意了&#xff0c;此时的V是个大写的V) 二、…

海外问卷调查项目拆解(操作全流程演示)保姆级攻略具体操作

海外问卷调查是怎么做的&#xff1f;好做吗&#xff1f; 关于这个问题&#xff0c;不管我说好做、或者不好做&#xff0c;都并不能解决问题&#xff1b; 所以&#xff0c;这篇文章直接把做题的全过程展现给你看&#xff0c;你自己去真实地感受一下&#xff0c;这个玩意儿到底…

华媒舍:3个科学指导,协助油管大V写下爆款文章

油管&#xff08;YouTube&#xff09;作为一个重要的视频分享平台&#xff0c;吸引了很多的观众和原创者。作为一位油管大V&#xff0c;你可能会一直在努力提升自己的文章质量以吸引更多的观众和订阅者。下面我们就为您提供三个科学指导&#xff0c;帮助自己写下更具有爆品发展…

浅析JavaWeb内存马基础原理与查杀思路

文章目录 前言Java内存马内存马分类&原理JavaWeb三大组件注入Servlet内存马注入Filter型内存马JAVA Agent内存马 哥斯拉木马0x01 WebShell0x02 MemShell0x03 FilterShell0x04 Arthas排查0x05 scanner查杀 总结 前言 几年前写过《Web安全-一句话木马》&#xff0c;主要介绍…

stargan项目实战及源码解读

数据及代码链接见文末 ​​​​​​​论文解析&#xff1a;Star GAN论文解析-CSDN博客 1.测试模块效果与实验分析 测试数据需要准备两个文件夹src&#xff08;源&#xff09;和ref&#xff08;目标&#xff09;&#xff0c;这两个文件夹下的文件夹名称代表各个domain。 运行测…

[lesson07]函数参数的扩展

函数参数的扩展 函数参数的默认值 C中可以在函数声明时为参数提供一个默认值 当函数调用时没有提供参数的值&#xff0c;则使用默认值 参数的默认值必须在函数声明中指定 函数默认参数的规则 参数的默认值必须从右向左提供函数调用时使用了默认值&#xff0c;则后续参数必…

数据结构:详解【树和二叉树】

1. 树的概念及结构&#xff08;了解&#xff09; 1.1 树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝…

小林coding图解计算机网络|基础篇02|键入网址到网页显示,期间发生了什么?

小林coding网站通道&#xff1a;入口 本篇文章摘抄应付面试的重点内容&#xff0c;详细内容还请移步&#xff1a;小林coding网站通道 文章目录 孤单小弟——HTTP真实地址查询——DNS指南好帮手——协议栈可靠传输——TCP远程定位——IP两点传输——MAC出口——网卡送别者——交…

一文搞懂 ThreadLocal

简介 ThreadLocal存取的数据&#xff0c;总是与当前线程相关&#xff0c;也就是说&#xff0c;JVM 为每个运行的线程&#xff0c;绑定了私有的本地实例存取空间&#xff0c;从而为多线程环境常出现的并发访问问题提供了一种隔离机制。 ThreadLocal的作用是提供线程内的局部变…

突破编程_前端_ACE编辑器(选中区域、跳转行以及点击事件)

1 选中区域 要在 ACE 编辑器中选中一个区域&#xff0c;通常需要使用编辑器的 selection 对象。 以下是一个简单的示例&#xff0c;展示了如何使用 ACE 编辑器的 API 来选中一个特定的区域&#xff1a; 初始化 ACE 编辑器&#xff1a;首先&#xff0c;需要在页面上初始化 AC…

arm开发板移植工具mkfs.ext4

文章目录 一、前言二、手动安装e2fsprogs1、下载源码包2、解压源码3、配置4、编译5、安装 三、移植四、验证五、总结 一、前言 在buildroot菜单中&#xff0c;可以通过勾选e2fsprogs工具来安装mkfs.ext4工具&#xff1a; Target packages -> Filesystem and flash utilit…