数据结构--红黑树(RBTree)

一、红黑树概念

1.1 什么是红黑树

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

2.1 红黑树的性质
  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个结点是红色的,则他的两个孩子是黑色的,及不存在两个连续的红色结点
  4. 对于每个叶子结点,从该结点到其所有后代叶结点的简单路径,均包含相同数目的黑色节点
  5. 每个叶子结点都是黑色的(此处的叶子节点指的是空节点)

思考:为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点 个数的两倍?

【解释】:由于红黑树要保证每条路径的黑色结点个数相同,且不能存在连续的两个红色结点,所以最短路径就是全黑,最长路径是一黑一红交替,这样红黑树的最长路径的极限也只能为最短路径的两倍

二、红黑树的实现

2.1 红黑树结点的定义
enum Color
{RED,BLACK
};
template<class K,class V>
struct RBTreeNode
{RBTreeNode(const pair<K,V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_col(RED){}struct RBTreeNode* _left;struct RBTreeNode* _right;struct RBTreeNode* _parent;pair<K, V> _kv;Color _col;
};

思考:在结点的定义中,为什么要将结点的默认颜色给成红色的?

【解释】:若将结点的颜色默认为黑色,由于要求每条路径的黑色结点个数相同,如果新插入一个结点,就会破坏这个性质,影响比较大,如果将结点的颜色默认为红色,那么最多也就会出现两个连续的红色结点,只会影响一条路径,只需要进行调整即可,影响比较小

2.2 红黑树的插入

1. 按照二叉搜索树的规则将结点插入到红黑树中

2. 检测插入结点后,红黑树的性质是否遭到破坏

因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何 性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:

约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点(因为cur与p一定为红色,g一定为黑色,我们只需要对u进行讨论)

  • 情况一:cur为红,p为红,g为黑,u存在且为红(假设uncle在g的右边)

注意:此时看到的树可能是一颗完整的树,也可能是一颗子树

解决方法:

        由于出现了连续的两个红色结点,我们只能将p变为黑色,因为如果cur为新增结点,将其变为黑色会破坏性质4,而将p变为黑色,会导致g的左右子树黑色节点不同,所以需要将u也变为黑色,而g有可能只是一颗子树,为了保证与其他路径的黑色节点个数相同,还需要将g变为红色如果g为根节点的话,再将其变为黑色即可。

  • 情况二:cur为红色,p为红色,g为黑色,u不存在或者u存在且为黑色(假设u在g的右边)

说明:u的情况存在两种

1.uncle不存在,那么cur一定是新增的那个结点,因为如果不是新增结点,那么cur和p至少有一个应该为黑色,不然就违反性质3了

2.uncle存在且为黑色,那么cur原来的颜色一定为黑色,现在看到cur的颜色为红色是因为cur子树在调整过程中将cur变为了红色

【解决办法】:

  • 如果cur为p的左孩子

将p变黑,g变红,以g作为父亲结点右单旋

  • 如果cur为p的右孩子

将cur变黑,g变红,先以p为父亲节点左单旋,在以g为父亲节右单旋

ps:uncle为g左边原理同上,对于旋转不清楚的可以参考

2.3 代码实现
bool Insert(const pair<K,V>& kv)
{if (_root == nullptr){_root = new Node(kv);_root->_col = 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);if (parent->_kv.first>kv.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续向上调整cur = grandfather;parent = cur->_parent;}else //uncle不存在或者uncle存在且为黑{if (cur == parent->_left){//      g//    p   u//  cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//      g//    p   u//      cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续向上调整cur = grandfather;parent = cur->_parent;}else //uncle不存在或者uncle存在且为黑{if (cur == parent->_right){//      g//    u   p//          cRotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//      g//    u   p//      cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;
}

三、红黑树的验证

红黑树的检测分为两步:

1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)

2. 检测其是否满足红黑树的性质

bool IsBalance()
{if (_root->_col == RED)return false;Node* cur = _root;int RefNum = 0; //作为路径黑色结点个数的参考while (cur){if (cur->_col == BLACK)RefNum++;cur = cur->_left;}return _IsBalance(_root,RefNum,0);
}bool _IsBalance(Node* root,int RefNum,int num)
{if (root == nullptr){//验证每条路径的黑色节点个数是否相同if (num != RefNum){cout << "黑色节点数不同" << endl;return false;}return true;}//验证是否存在两个连续的红色节点if (root->_col == RED && root->_parent->_col == RED){cout << "连续两个红色" << root->_kv.first << endl;return false;}if (root->_col == BLACK)num++;return _IsBalance(root->_left, RefNum, num) && _IsBalance(root->_right, RefNum, num);
}

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

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

相关文章

Facebook国内账户与 Facebook海外账户的区别

Facebook国内户的封户速度和频率有时可谓令人崩溃&#xff0c;为这种情况伤脑筋的朋友&#xff0c;不妨考虑一下Facebook海外户&#xff0c;既不限额&#xff0c;又更稳定..... Facebook&#xff0c;Google 开企业广告账户/游戏代投 &#xff0b;V:Ukvo77 TG&#xff1a;ukv…

【C++】 C++ 编写 鸡兔同笼程序

文章目录 “鸡兔同笼”问题是一个经典的数学问题&#xff0c;要求根据总头数和总腿数来计算鸡和兔的数量。假设鸡有 2 条腿&#xff0c;兔有 4 条腿。可以通过以下步骤求解这个问题&#xff1a; 1 .设鸡的数量为 x&#xff0c;兔的数量为 y。2.根据题意&#xff0c;我们有以下…

美国多IP服务器为企业的数据分析提供了强大的技术支持

美国多IP服务器为企业的数据分析提供了强大的技术支持 在当今数字化时代&#xff0c;数据分析已经成为企业决策和战略规划的核心。而美国多IP服务器则为企业提供了强大的技术支持&#xff0c;帮助它们有效地进行数据分析&#xff0c;从而更好地理解市场、优化运营&#xff0c;…

分享如何通过定时任务调用lighthouse前端测试脚本+在持续集成测试中调用lighthouse前端测试脚本

最近写了个小工具来优化lighthouse在实际工作中的使用&#xff0c;具体实现了&#xff1a;通过定时任务调用前端测试脚本在持续集成测试中调用前端测试脚本。由于在公司中已经应用&#xff0c;所以就不能提供源码了&#xff0c;这里简单说一下实现思路&#xff0c;希望可以帮助…

C语言 | Leetcode C语言题解之第89题格雷编码

题目&#xff1a; 题解&#xff1a; int* grayCode(int n, int* returnSize) {int ret_size 1 << n;int *ret (int *)malloc(ret_size * sizeof(int));for (int i 0; i < ret_size; i) {ret[i] (i >> 1) ^ i;}*returnSize ret_size;return ret; }

数据结构——队列(链表实现)

一、队列的特点 先进先出 二、队列的代码 typedef int QDataType;// 链式结构&#xff1a;表示队列 typedef struct QListNode {struct QListNode* next;QDataType data; }QNode;// 队列的结构 typedef struct Queue {QNode* front; //指向队列的第一个结点QNode* rear;//指…

Linux 生态与工具

各位大佬好 &#xff0c;这里是阿川的博客 &#xff0c; 祝您变得更强 个人主页&#xff1a;在线OJ的阿川 大佬的支持和鼓励&#xff0c;将是我成长路上最大的动力 阿川水平有限&#xff0c;如有错误&#xff0c;欢迎大佬指正 目录 Linux生态简介:Linux工具lrzsz&#xff…

Python100个库分享第23个—wordcloud(词云图)

目录 专栏导读库的介绍库的安装基础使用1&#xff1a;将TXT文本转为词云图基础使用2&#xff1a;使用自定义字体和形状基础使用3&#xff1a;中文词云图停用词(中英文版)-代码是中文版总结 专栏导读 &#x1f338; 欢迎来到Python办公自动化专栏—Python处理办公问题&#xff0…

FullCalendar日历组件集成实战(3)

背景 有一些应用系统或应用功能&#xff0c;如日程管理、任务管理需要使用到日历组件。虽然Element Plus也提供了日历组件&#xff0c;但功能比较简单&#xff0c;用来做数据展现勉强可用。但如果需要进行复杂的数据展示&#xff0c;以及互动操作如通过点击添加事件&#xff0…

通用人工智能将如何重塑未来

通用人工智能(AGI)是一种人工智能&#xff0c;具有与人类一样的获取知识、应用知识解决问题和理解能力。与专门处理受限任务的狭义人工智能系统不同&#xff0c;AGI寻求发展先进的认知技能&#xff0c;以促进在不同情况下完成复杂任务。AGI是一种人工智能&#xff0c;试图模仿人…

Java基于SSM的在线课堂微信小程序【附源码、文档】

博主介绍&#xff1a;✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3…

直流无刷电机控制(一)六步换相(有感霍尔)附六步换相实现代码

直流无刷电机概述 直流无刷电机的转子为永磁铁&#xff0c;定子为换相线圈&#xff0c;有别于有刷电机通过电刷或者换向器换相&#xff0c;无刷电机通过控制器电子换相。 极对数 直流无刷电机采用永磁铁作为转子&#xff0c;一对NS磁极为一极对&#xff0c;为了使电机运转更…