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

目录

(一)红黑树的定义

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.rhkb.cn/news/111560.html

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

相关文章

扩散模型实战(八):微调扩散模型

推荐阅读列表&#xff1a; 扩散模型实战&#xff08;一&#xff09;&#xff1a;基本原理介绍 扩散模型实战&#xff08;二&#xff09;&#xff1a;扩散模型的发展 扩散模型实战&#xff08;三&#xff09;&#xff1a;扩散模型的应用 扩散模型实战&#xff08;四&#xf…

yolov5和yolov7部署的研究

1.结论 onnx推理比torch快3倍, openvino比onnx快一丢丢。 | yolov7.pt 转 onnx python export.py --weights best_31.pt --grid --end2end --simplify --topk-all 10 --iou-thres 0.65 --conf-thres 0.65 --img-size 320 320 --max-wh 200可以看到yolov7的 onnx是包括nms…

【Unity小技巧】手戳一个简单易用的游戏UI框架(附源码)

文章目录 前言整套框架分为三大部分框架代码调用源码参考完结 前言 开发一款游戏美术成本是极其高昂的&#xff0c;以我们常见的宣传片CG为例&#xff0c;动辄就要成百上千万的价格&#xff0c;因此这种美术物料一般只会放在核心剧情节点&#xff0c;引爆舆论&#xff0c;做高…

MATLAB中符号变量的使用方法解析

简介 MATLAB中常常使用符号变量&#xff0c;这里定义符号变量的函数是syms 使用方法如下 syms x y z 其中&#xff0c;x、y、z 是符号变量&#xff0c;可以是任意字母、数字或下划线组合而成的字符串。 举例1&#xff1a; 代码 以下是一个简单的例子&#xff0c;演示如何…

WebSocket- 前端篇

官网代码 // 为了浏览器兼容websocketconst WebSocket window.WebSocket || window.MozWebSocket// 创建连接 this.socket new WebSocket(ws://xxx)// 连接成功this.socket.onopen (res)>{console.log(websocket 连接成功)this.socket.send(入参字段) // 传递的参数字段}…

强化自主可控,润开鸿发布基于RISC-V架构的开源鸿蒙终端新品

2023 RISC-V中国峰会于8月23日至25日在北京召开,峰会以“RISC-V生态共建”为主题,结合当下全球新形势,把握全球新时机,呈现RISC-V全球新观点、新趋势。本次大会邀请了RISC-V国际基金会、业界专家、企业代表及社区伙伴等共同探讨RISC-V发展趋势与机遇,吸引超过百余家业界企业、高…

出现ZooKeeper JMX enabled by default这种错误的解决方法

系列文章专栏 学习以来遇到的bug/问题专栏 文章目录 系列文章专栏 前言 一 问题描述 二 解决方法 2.1 可能的原因分析 2.2 小编的问题解决方法 First&#xff1a;检查/etc/profile里面zookeeper的环境变量配置 Second&#xff1a;检查 zookeeper/conf/zoo.cfg里面的d…

minikube mac 启动

系统信息如下 最开始使用的minikube是1.22.0版本&#xff0c;按照如下命令启动&#xff1a; minikube start --memory7851 --cpus4 --image-mirror-countrycn遇到了下面一些问题&#xff1a; 1、拉取coredns:v1.8.0镜像失败 Error response from daemon: manifest for regis…

Tensorflow调用训练好的yolov5模型进行推理

文章目录 1、安装TensorFlow-GPU版本1.2、验证是否安装正常 2、将训练好的pt文件转换成onnx文件2.2、什么是Onnx模型和Tensorflow模型2.1、将onnx文件转换成pb文件 1、安装TensorFlow-GPU版本 1、创建虚拟环境python3.8 conda create -n TF2.4 python3.82、进入虚拟环境 conda…

智安网络|探索物联网架构:构建连接物体与数字世界的桥梁

物联网是指通过互联网将各种物理设备与传感器连接在一起&#xff0c;实现相互通信和数据交换的网络系统。物联网架构是实现这一连接的基础和框架&#xff0c;它允许物体与数字世界之间的互动和协作。 一、物联网架构的概述 物联网架构是一种分层结构&#xff0c;它将物联网系…

python面试:使用cProfile剖析程序性能

我们需要安装tuna&#xff1a;pip install tuna 程序执行完毕后&#xff0c;我们会得到一个results.prof&#xff0c;在CMD中输入指令&#xff1a;“tuna results.prof”。 import time import cProfile import pstatsdef add(x, y):resulting_sum 0resulting_sum xresulti…

(Windows )本地连接远程服务器(Linux),免密码登录设置

在使用VScode连接远程服务器时&#xff0c;每次打开都要输入密码&#xff0c;以及使用ssh登录或其它方法登录&#xff0c;都要本地输入密码&#xff0c;这大大降低了使用感受&#xff0c;下面总结了免密码登录的方法&#xff0c;用起来巴适得很&#xff0c;起飞。 目录 PowerSh…

2024年java面试(四)--spring篇

文章目录 1.BeanFactory 和 FactoryBean 的区别2.BeanFactory和ApplicationContext有什么区别?3.RequestBody、RequestParam、ResponseBody4.cookie和session的区别5.Servlet的生命周期6.Jsp和Servlet的区别7.SpringMvc执行流程8.RequestMapping是怎么使用9.如果一个接口有多个…

爬虫逆向实战(二十七)--某某招标投标网站招标公告

一、数据接口分析 主页地址&#xff1a;某网站 1、抓包 通过抓包可以发现数据接口是page 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”模块可以发现&#xff0c;请求参数是一整个密文 请求头是否加密&#xff1f; 无响应是否加密&#xff1f; 通…

Mac下使用Homebrew安装MySQL5.7

Mac下使用Homebrew安装MySQL5.7 1. 安装Homebrew & Oh-My-Zsh2. 查询软件信息3. 执行安装命令4. 开机启动5. 服务状态查询6. 初始化配置7. 登录测试7.1 终端登录7.2 客户端登录 参考 1. 安装Homebrew & Oh-My-Zsh mac下如何安装homebrew MacOS安装Homebrew与Oh-My-Zsh…

使用DataX对MySQL 8.1进行数据迁移

1. 环境准备 1.1 下载DataX 这里采用直接下载的方式&#xff1a;https://datax-opensource.oss-cn-hangzhou.aliyuncs.com/202308/datax.tar.gz&#xff0c;不过这个包是真的有点大。 1.2 安装Python Python下载地址&#xff1a;https://www.python.org/downloads/ 安装的时…

运维Shell脚本小试牛刀(一)

运维Shell脚本小试牛刀(一) 运维Shell脚本小试牛刀(二) 一: Shell中循环剖析 for 循环....... #!/bin/bash - # # # # FILE: countloop.sh # USAGE: ./countloop.sh # DESCRIPTION: # OPTIONS: ------- # …

泰迪大数据实训平台产品介绍

大数据产品包括&#xff1a;大数据实训管理平台、大数据开发实训平台、大数据编程实训平台等 大数据实训管理平台 泰迪大数据实训平台从课程管理、资源管理、实训管理等方面出发&#xff0c;主要解决现有实验室无法满足教学需求、传统教学流程和工具低效耗时和内部教学…

C++ 读写Excel LibXL库的使用附注册码(key)

LibXL是一款用于读写处理 Excel 文件的库,支持C, C++, C#,Python等语言。并且支持多个平台windows、Linux、Mac等,它提供了一系列的API,让开发人员可以方便地读取、修改和创建Excel文件。 一、关于库的key与使用 1.价值3000多的key 但是这个库并不是免费的,使用此库需要…

[Android AIDL] --- AIDL原理简析

上一篇文章已经讲述了如何在Android studio中搭建基于aidl的cs模型框架&#xff0c;只是用起来了&#xff0c;这次对aidl及cs端如何调用的原理进行简单分析 1 创建AIDL文件 AIDL 文件可以分为两类。 一类是用来定义接口方法&#xff0c;声明要暴露哪些接口给客户端调用&#…