【数据结构与算法】—— 手撕红黑树

目录

(一)红黑树的定义

1、红黑树的引入

2、红黑树的概念

3、红黑树的性质

(二)红黑树的操作

1、红黑树节点的定义

2、红黑树的插入操作

1️⃣ 思路

2️⃣ 代码实现

3、红黑树的删除操作(了解)

4、红黑树与AVL树的比较

5、红黑树的验证

总结


(一)红黑树的定义

1、红黑树的引入

为了保持 AVL 树的平衡性,插入和删除操作后,非常频繁地调整全树整体拓扑结构,代价较大。为此在 AVL 树的平衡标准上进一步放宽条件,引入了红黑树的结构。

2、红黑树的概念

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

3、红黑树的性质

一棵红黑树是满足如下红黑性质的二叉排序树:

  • ①每个结点或是红色,或是黑色的
  • ②根结点是黑色的
  • ③叶结点(虚构的外部结点、 NULL 结点)都是黑色的
  • ④不存在两个相邻的红结点(即红结点的父结点和孩子结点均是黑色的)
  • ⑤对每个结点,从该结点到任一叶结点的简单路径上,所含黑结点的数量相同

与折半查找树和 B 树类似,为了便于对红黑树的实现和理解,引入了 n +1个外部叶结点,以保证红黑树中每个结点(内部结点)的左、右孩子均非空。下图所示是一棵红黑树:

从某结点出发(不含该结点)到达一个叶结点的任一简单路径上的黑结点总数称为该结点的黑高(记为 bh ),黑高的概念是由性质⑤确定的。根结点的黑高称为红黑树的黑高。

结论1:从根到叶结点的最长路径不大于最短路径的2倍。

  1. 由性质⑤,当从根到任一叶结点的简单路径最短时,这条路径必然全由黑结点构成;
  2. 由性质④,当某条路径最长时,这条路径必然是由黑结点和红结点相间构成的,此时红结点和黑结点的数量相同;
  3. 上图中的 13-8-1 和13 - 17 - 25 - 27就是这样的两条路径。

结论2:有n个内部结点的红黑树高度 h <=2log(N+1)

由此可见,红黑树的"适度平衡",由 AVL 树的"高度平衡",降低到"任一结点左右子树的高度,相差不超过2倍",也降低了动态操作时调整的频率。对于一棵动态查找树,如果插入和删除操作比较少,查找操作比较多,采用 AVL 树比较合适,否则采用红黑树更合适。但由于维护这种高度平衡所付出的代价比获得的效益大得多,红黑树的实际应用更广泛, C ++中的 map 和 set ( Java 中的 TreeMap 和 TreeSet )就是用红黑树实现的。
 


(二)红黑树的操作

1、红黑树节点的定义

enum Colour
{RED,BLACK,
};// 红黑树节点的定义template<class K, class V>
struct RBTreeNode
{ RBTreeNode<K, V>* _left;   // 节点的左孩子RBTreeNode<K, V>* _right;  // 节点的右孩子RBTreeNode<K, V>* _parent; // 节点的双亲(红黑树需要旋转,为了实现简单给出该字段)pair<K, V> _kv;            // 节点的值域Colour _col;               // 节点的颜色RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED){}
};

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

红黑树是一种自平衡的二叉搜索树,它通过在插入和删除节点时进行一系列的旋转和重新着色操作来维持平衡性质。根据红黑树的性质,每个节点要么是红色,要么是黑色。

在红黑树的插入和删除操作中,为了保持树的平衡,需要对节点进行旋转和着色。将新插入的节点默认着色为红色有以下几个原因:

  1. 红色节点具有更多的调整空间:将节点默认着色为红色,可以让新节点具有更多的调整空间,有利于保持树的平衡性。红色节点的插入和删除操作对树的平衡影响较小,因为红色节点可以在需要时进行旋转和着色调整。

  2. 简化插入操作的修正过程:新节点默认为红色,可以简化插入操作的修正过程。根据红黑树的性质,插入红色节点后只需要考虑和父节点的关系,而不需要像黑色节点那样考虑更多的调整情况。这样可以减少修正操作的复杂性和开销。

  3. 减少整体平衡操作的次数:将新节点默认设置为红色,可以减少整体平衡操作的次数。由于红色节点的插入和删除操作对树的平衡影响较小,相比于将新节点默认设置为黑色,将其默认设置为红色可以减少平衡操作的频率和开销。

总的来说,将节点的默认颜色设置为红色是为了在红黑树等自平衡数据结构中简化插入和删除操作的修正过程,同时减少整体平衡操作的次数,提高了算法的效率和性能。

2、红黑树的插入操作

1️⃣ 思路

红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:

  • 1.按照二叉搜索的树规则插入新节点(这个我们之前说过,这里就不过多解释了)
  • 2.检测新节点插入后,红黑树的性质是否造到破坏(重点讲解

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

约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点
 

  • 情况一: cur为红,p为红,g为黑,u存在且为红

 解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整

  •  情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑

1.p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,

2.p为g的右孩子,cur为p的右孩子,则进行左单旋转

3.p、g变色--p变黑,g变红

  • 情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑
     

 针对每种情况进行相应的处理即可


2️⃣ 代码实现

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 (grandfather->_left == parent){Node* uncle = grandfather->_right;// 情况1:u存在且为红,变色处理,并继续往上处理if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;// 继续往上调整cur = grandfather;parent = cur->_parent;}else // 情况2+3:u不存在/u存在且为黑,旋转+变色{//     g//   p   u// c if (cur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//     g//   p   u//     cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;//parent->_col = RED;grandfather->_col = RED;}break;}}else // (grandfather->_right == parent){//    g//  u   p//        cNode* uncle = grandfather->_left;// 情况1:u存在且为红,变色处理,并继续往上处理if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;// 继续往上调整cur = grandfather;parent = cur->_parent;}else // 情况2+3:u不存在/u存在且为黑,旋转+变色{//    g//  u   p//        cif (cur == parent->_right){RotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{//    g//  u   p//    cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;}

3、红黑树的删除操作(了解)

红黑树的删除本节不做讲解,有兴趣的同学可参考:《算法导论》或者《STL源码剖析》
红黑树的删除


4、红黑树与AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(log_2 N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

5、红黑树的验证

红黑树的检测分为两步:

  • 1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
  • 2. 检测其是否满足红黑树的性质
     
	void Inorder(){_Inorder(_root);}bool IsBalance(){if (_root && _root->_col == RED){cout << "根节点颜色是红色" << endl;return false;}int benchmark = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK)++benchmark;cur = cur->_left;}// 连续红色节点return _Judge(_root, 0, benchmark);}bool _Judge(Node* root, int blackNum, int benchmark){if (root == nullptr){if (blackNum != benchmark){cout << "某条路径黑色节点的数量不相等" << endl;return false;}return true;}if (root->_col == BLACK){++blackNum;}if (root->_col == RED&& root->_parent&& root->_parent->_col == RED){cout << "存在连续的红色节点" << endl;return false;}return _Judge(root->_left, blackNum, benchmark)&& _Judge(root->_right, blackNum, benchmark);}

总结

以上便是关于红黑树的介绍。感谢大家的观看与支持!!!

 

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

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

相关文章

SDXL:Improving latent diffusion models for high-resolution image synthesis

文生图模型之SDXL - 知乎之前的文章 文生图模型之Stable Diffusion已经介绍了比较火的文生图模型Stable Diffusion&#xff0c;近期Stability AI又发布了新的升级版本SDXL。目前SDXL的代码、模型以及技术报告已经全部开源&#xff1a;官方代码&#xff1a;https://github.…htt…

伴儿行“共享七彩童年”红色教育讲堂篇

迎着徐徐海风&#xff0c;一群青少年缓缓漫步于刘公岛的海岸线&#xff0c;在威海市环翠区伴儿行青少年服务中心老师的带领下&#xff0c;一场沉浸式的红色教育讲堂在百年后的甲午海战战场拉开帷幕。 从甲午海战的前期国际形势讲到当今的国际国内形势&#xff0c;从甲午清朝海军…

「Qt」文件读写操作

0、引言 我们知道 C 和 C 都提供了文件读写的类库&#xff0c;不过 Qt 也有一套自己的文件读写操作&#xff1b;本文主要介绍 Qt 中进行文件读写操作的类 —— QFile。 1、QFileDialog 文件对话框 一般的桌面应用程序&#xff0c;当我们想要打开一个文件时&#xff0c;通常会弹…

华为将收取蜂窝物联网专利费,或将影响LPWAN市场发展

近日&#xff0c;华为正式公布了其4G和5G手机、Wi-Fi6设备和物联网产品的专利许可费率&#xff0c;其中包含了长距离通信技术蜂窝物联网。作为蜂窝物联网技术的先驱&#xff0c;华为是LTE Category NB (NB-IoT)、LTE Category M和其他4G物联网标准的主要贡献者。 在NB-IoT领域…

Linux TCP编程流程

一、TCP编程流程 TCP 提供的是面向连接的、可靠的、字节流服务。TCP的服务器端和客户端编程流程如下&#xff1a; 1.socket()方法 用来创建一个套接字&#xff0c;有了套接字就可以通过网络进行数据的收发。这也是为什么进行网络通信的程序首先要创建一个套接字。创建套接字时…

摩托车外廓尺寸检测软件

本系统为摩托车外廓尺寸检测软件&#xff0c;该系统共涉及两种测量方法&#xff1a;自动测量和手动测量&#xff0c;旨在测量出每一台摩托车的外廓尺寸&#xff0c;包括但不限于摩托车的车长、车宽、车高、轮距、前悬、后悬、前伸距等需要测量的参数&#xff0c;可通过运行软件…

工业生产全面感知!工业感知云来了

面向工业企业数字化转型需求&#xff0c;天翼物联基于感知云平台创新能力和5G工业物联数采能力&#xff0c;为客户提供工业感知云服务&#xff0c;包括工业泛协议接入、感知云工业超轻数采平台、工业感知数据治理、工业数据看板四大服务&#xff0c;构建工业感知神经系统新型数…

【SpringSecurity】五、UserDetails接口和UserDetailsService接口

文章目录 1、SpringSecurity原理2、UserDetails接口3、UserDetailService接口4、权限配置 1、SpringSecurity原理 Spring Security是做安全访问控制&#xff0c;对所有进入系统的请求进行拦截&#xff0c;并做校验&#xff0c;这可以通过Filter或者AOP实现&#xff0c;Spring …

设计模式(9)建造者模式

一、 1、概念&#xff1a;将一个复杂对象的构造与它的表示分离&#xff0c;使得同样的构造过程可以创建不同的表示。建造者模式主要用于创建一些复杂的对象&#xff0c;这些对象内部构建间的顺序通常是稳定的&#xff0c;但对象内部的构建通常面临着复杂的变化&#xff1b;建造…

R语言机器学习方法在生态经济学领域

近年来&#xff0c;人工智能领域已经取得突破性进展&#xff0c;对经济社会各个领域都产生了重大影响&#xff0c;结合了统计学、数据科学和计算机科学的机器学习是人工智能的主流方向之一&#xff0c;目前也在飞快的融入计量经济学研究。表面上机器学习通常使用大数据&#xf…

【测试】pywinauto的简单使用(安装、常用对象、元素控件、鼠标操作、键盘操作)

1.说明 pywinauto是一个用于自动化Python 模块&#xff0c;适合Windows系统的软件&#xff08;GUI&#xff09;&#xff0c;可以通过Pywinauto遍历窗口&#xff08;对话框&#xff09;和窗口里的控件&#xff0c;也可以控制鼠标和键盘输入&#xff0c;所以它能做的事情比之前介…

【微服务】spring 条件注解从使用到源码分析详解

目录 一、前言 二、spring 条件注解概述 2.1 条件注解Conditional介绍 2.2 Conditional扩展注解 2.2.1 Conditional扩展注解汇总 三、spring 条件注解案例演示 3.1 ConditionalOnBean 3.2 ConditionalOnMissingBean 3.2.1 使用在类上 3.2.2 使用场景补充 3.3 Condit…