红黑树的简单介绍

红黑树

红黑树的概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
例如:
下图就是一个红黑树,同时也是一颗二叉搜索树
和AVL树不同的是,AVL树依靠着平衡因子的限制的平衡性比红黑树要更高
在这里插入图片描述

红黑树的性质

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

我们要尤其注意一个点:
红黑树的叶子节点是空节点,而非我们之前传统意义上的叶子节点

对于性质3的简化就是,一条路径上不能出现连续的红节点

基于上面给出的性质,大家可以思考一个问题:

那么一颗红黑树的最短路径和最长路径分别有什么特点呢?
大家仔细思考就会返现,由于每条路径必须要有相同的黑节点,且不能出现连续的红节点,就可以总结出来:
最短路径全是黑节点,最长路径是一黑一红节点相间

可能大家会有一个问题:为什么有了AVL树还有搞一个红黑树呢,而且他的平衡性还没有AVL树高

确实红黑树的搜索时间复杂度没有AVL树这么快,但是红黑树的搜索效率和AVL树可以近似看作相等,但是红黑树不需要那么多的旋转来调平衡,因为红黑树可以允许最长路径是最短路径的2倍,他的要求并没有AVL树那么严格,所以红黑树的旋转次数要比AVL树少很多,效率自然就提升了,故而实际应用中红黑树要比AVL树用的更多一些。map和set的底层实现都是用红黑树而并非AVL树所实现的。

红黑树的定义

根据上面的红黑树的性质和我们之前学习的AVL树的知识的铺垫,我们就可以很快的将红黑树的基本框架搭起来:

与AVL树的平衡因子不同,红黑树除了节点外还要枚举节点的颜色
我们将黑色和红色先进行枚举

enum Color { RED, BLACK };

然后进行树的节点的定义:

// 红黑树节点的定义
template<class ValueType>
struct RBTreeNode
{RBTreeNode(const ValueType& data = ValueType(),Color color = RED): _left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _color(color){}RBTreeNode<ValueType>* _left;   // 节点的左孩子RBTreeNode<ValueType>* _right;  // 节点的右孩子RBTreeNode<ValueType>* _parent; // 节点的双亲(红黑树需要旋转,为了实现简单给出该字段)ValueType _data;            // 节点的值域Color _color;               // 节点的颜色
};

大家可以看到我们在节点的颜色的初始化时的时候给了缺省参数是红色,这是为什么呢?

大家可以想一想,如果我们给的节点是黑色,那么无论新增到哪一颗红黑树上都会导致这颗红黑树不平衡,但是使用红节点只是可能导致不平衡,所以我们当然优先使用红节点!

例如:
下图中新增红节点不一定会导致红黑树不平衡,但是如果新增的节点颜色是黑色,那么一定要进行操作来保持这棵树的平衡
在这里插入图片描述

红黑树的插入

和AVL树一样,红黑树的插入操作可以分为两步:

1.按照二叉搜索的树规则插入新节点

我们先插入节点,不管平衡性

template<class T>
class RBTree
{typedef RBTreeNode<T> Node;
public:bool insert(const T& key){if (_root == nullptr){_root = new Node(key);_root->_color = RED;return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_data > key){parent = cur;cur = cur->_left;}else if (cur->_data < key){parent = cur;cur = cur->_right;}else{return false;}}cur = new Node(key);cur->_color = RED;if (parent->_data > key){parent->_left = cur;cur->_parent = parent;}else{parent->_right = cur;cur->_parent = parent;}}private:Node* _root = nullptr;};

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

由于需要双亲节点的兄弟和父亲节点,我们制定了简写:
cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点

注意,下面所有图中的树可能是一整棵树,也有可能是一颗子树

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

将u和p变黑,g变红,如果g是根节点的话,调整完成后g节点必须变黑

如果g还有双亲节点的话,并且g的parent是红色的话就继续向上调整
在这里插入图片描述
情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑

p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,
p为g的右孩子,cur为p的右孩子,则进行左单旋转
p、g变色–p变黑,g变红

在这里插入图片描述
情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑

p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,
p为g的右孩子,cur为p的左孩子,则针对p做右单旋转
则转换成了情况2
在这里插入图片描述

完整插入代码如下:

bool insert(const T& key)
{if (_root == nullptr){_root = new Node(key);_root->_color = RED;return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_data > key){parent = cur;cur = cur->_left;}else if (cur->_data < key){parent = cur;cur = cur->_right;}else{return false;}}cur = new Node(key);cur->_color = RED;if (parent->_data > key){parent->_left = cur;cur->_parent = parent;}else{parent->_right = cur;cur->_parent = parent;}while (parent && parent->_color == RED){Node* grandfather = parent->_parent;//parent是g的左孩子if (parent->_left == grandfather->_left){//结构//     g//  p     u//cNode* uncle = grandfather->_right;if (uncle && uncle->_color == RED){//进行变色parent->_color = uncle->_color = BLACK;grandfather->_color = RED;cur = grandfather;parent = cur->_parent;}//uncle不存在else{if (cur == parent->_left){//单旋//    g//  p//cRotateR(grandfather);parent->_color = BLACK;grandfather->_color = RED;}else{//双旋//     g//  p//     cRotateL(parent);RotateR(grandfather);cur->_color = BLACK;grandfather->_color = RED;}break;}}else // parent == grandfather->_right {// g // u p// c Node* uncle = grandfather->_left;if (uncle && uncle->_color == RED){// 变色 parent->_color = uncle->_color = BLACK;grandfather->_color = RED;// 继续往上处理 cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){RotateL(grandfather);parent->_color = BLACK;grandfather->_color = RED;}else{// g // u p // c // RotateR(parent);RotateL(grandfather);cur->_color = BLACK;grandfather->_color = RED;}break;}}_root->_color = BLACK;return true;}
}
红黑树的迭代器

迭代器是很重要的一个部分,没有迭代器一切容器都不好用

template<class T>
struct _treeiterator
{typedef RBTreeNode<T> Node;typedef _treeiterator<T> Self;Node* _node;_treeiterator(Node* node):_node(node){ }T& operator*(){return _node->_data;}T* operator&(){return &_node->_data;}Self operator++(){if (_node->_right){Node* cur = _node->_right;while (cur->_left){cur = cur->_left;}_node = cur;}else{Node* cur = _node->_left;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}bool operator != (const Self& s){return _node != s._node;}bool operator == (const Self & s){return _node == s._node;}
};typedef treeiterator<T> iterator;iterator begin()
{Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return iterator(cur);
}iterator end()
{return iterator(nullptr);
}
红黑树的验证

红黑树的检测分为两步:

1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
2. 检测其是否满足红黑树的性质

由于博主的能力有限,本篇博文的分享到这里就结束了,感谢大家的支持!

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

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

相关文章

Python类 __init__() 是一个特殊的方法

设计者&#xff1a;ISDF工软未来 版本&#xff1a;v1.0 日期&#xff1a;2024/3/5__init__() 是一个特殊的方法 类似c# C的构造函数 两头都包含两个下划线&#xff0c;这是约定&#xff0c;用于与普通的函数保持区分class User:用户类def __init__(self,first_name,last_name):…

Linux 运维:CentOS/RHEL防火墙和selinux设置

Linux 运维&#xff1a;CentOS/RHEL防火墙和selinux设置 一、防火墙常用管理命令1.1 CentOS/RHEL 7系统1.2 CentOS/RHEL 6系统 二、临时/永久关闭SELinux2.1 临时更改SELinux的执行模式2.2 永久更改SELinux的执行模式 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;…

Finetuning Large Language Models: Sharon Zhou

Finetuning Large Language Models 课程地址&#xff1a;https://www.deeplearning.ai/short-courses/finetuning-large-language-models/ 本文是学习笔记。 Goal&#xff1a; Learn the fundamentals of finetuning a large language model (LLM). Understand how finetu…

STM32(16)使用串口向电脑发送数据

发送字节 发送数组 发送字符和字符串 字符&#xff1a; 字符串&#xff1a; 字符串在电脑中以字符数组的形式存储

ElasticSearch之分布式模型介绍,选主,脑裂

写在前面 本文看下es分布式模型相关内容。 1&#xff1a;分布式模型 1.1&#xff1a;分布式特征 支持水平扩展&#xff0c;可以存储PB级别数据&#xff0c;每个就能都有自己唯一的名称,默认名称时elasticsearch&#xff0c;可以通过配置文件&#xff0c;如cluster.name: my…

PowerBI怎么修改数据库密码

第一步&#xff1a;点击转换数据 第二步&#xff1a;点击数据源设置 第三步&#xff1a;点击编辑权限 第四步&#xff1a;点击编辑 第五步&#xff1a;输入正要修改的密码就可以了

STM32启动过程及反汇编

STM32从Flash启动的过程&#xff0c;主要是从上电复位到main函数的过程&#xff0c;主要有以下步骤&#xff1a; 1.初始化堆栈指针 SP_initial_sp&#xff0c;初始化 PC 指针Reset_Handler 2.初始化中断向量表 3.配置系统时钟 4.调用 C 库函数_main 初始化用户堆栈&#xf…

SAP HANA中PAL算法使用入门

1 应用场合 SAP HANA作为一款内存数据库产品, 使得数据常驻内存, 物理磁盘的存储作为数据备份与日志记录, 以防断电内存中数据丢失. 这种构架大大的缩短了数据存取的时间, 使得SAP HANA很”高速”. 在传统数据模型中,数据库只是作为存取数据一个工具,对于类似下图所示的应用, 客…

星瑞格数据库管理系统

一. 产品介绍 随着信息化的到来&#xff0c;数据安全成为保障信息化建设的一个关键问题&#xff1b;数据库作为信息化系统的基础软件其自身安全以及对数据的保障是至关重要。现阶段国内重要部门的信息系统存放着大量敏感数据&#xff0c;为了保障其数据的安全性&#xff0c;使用…

11、电源管理入门之Regulator驱动

目录 1. Regulator驱动是什么? 2. Regulator框架介绍 2.1 regulator consumer 2.2 regulator core 2.3 regulator driver 3. DTS配置文件及初始化 4. 运行时调用 5. Consumer API 5.1 Consumer Regulator Access (static & dynamic drivers) 5.2 Regulator Outp…

基于springboot+vue的美食烹饪互动平台

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

Tomcat+Nginx的动静分离

1.反向代理多机 实验&#xff1a;Nginx要开启upstream(负载均衡)、location(url链接)、proxy_pass(反向代理) 配置&#xff1a;7-3做代理服务器&#xff1b;7-1 和 7-2做Tomcat服务器 关闭防火墙和selinux 1.准备配置 7-3安装nginx&#xff1b;7-1 和 7-2安装Tomcat&#xff…

章鱼网络 Community Call #18|Omnity 将首先支持 Runes 协议资产跨链

香港时间2024年2月8日12点&#xff0c;章鱼网络举行第18期 Community Call。 2024年&#xff0c;我们打开一个良好的局面&#xff1a;$NEAR Restaking 已经完成第三方审计&#xff0c;并且经过几次迭代&#xff0c;进入了正式稳定运行的阶段。更重要的是&#xff0c;我们宣布了…

对话框

1.焦点变更监听器 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.android.com/apk/res-auto"xmlns:tools"http://schem…

appium解锁android真机系统的屏幕

在使用appium进行app自动化操作的过程中&#xff0c;经常遇到的第一个难题就是如何解锁系统屏幕&#xff0c;也就是亮屏解锁。 实际上解决办法如下&#xff1a;在desired_capabilities中增加两个参数unlockType和unlockKey&#xff0c;类似的示例代码如下&#xff1a; desire…

一文弄懂回溯算法(例题详解)

目录 什么是回溯算法&#xff1a; 子集问题&#xff1a; 子集问题II(元素可重复但不可复选): 组合问题&#xff1a; 组合问题II(组合总和): 组合问题III(组合总和II): 排列问题&#xff1a; 排列问题II(元素可重复但不可复选): 排列问题III(元素无重复但可复选): 最后…

electron+vue3全家桶+vite项目搭建【29】封装窗口工具类【3】控制窗口定向移动

文章目录 引入实现效果思路声明通用的定位对象主进程模块渲染进程测试效果 引入 demo项目地址 窗口工具类系列文章&#xff1a; 封装窗口工具类【1】雏形 封装窗口工具类【2】窗口组&#xff0c;维护窗口关系 封装窗口工具类【3】控制窗口定向移动 很多时候&#xff0c;我们想…

【学习心得】网站运行时间轴(爬虫逆向)

一、网站运行时间轴 掌握网站运行时间轴&#xff0c;有助于我们对“请求参数加密”和“响应数据加密”这两种反爬手段的深入理解。 二、从网站运行的时间轴角度来理解两种反爬手段 1、加载HTML&#xff1a; 这是浏览器访问网站时的第一步&#xff0c;服务器会返回基础…

javascrip几种基本的设计模式

单例模式 ES5 function Duck1(name:string){this.namenamethis.instancenull }Duck1.prototype.getNamefunction(){console.log(this.name) }Duck1.getInstancefunction(name:string){if(!this.instance){this.instance new Duck1(name)} } const aDuck1.getInstance(a) const…

【系统架构设计师考试大纲】

曾梦想执剑走天涯&#xff0c;我是程序猿【AK】 目录 简述概要知识图谱考试目标考试要求考试题目题型分析计算机基础知识&#xff08;20%&#xff09;信息化战略与规划&#xff08;9%&#xff09;软件工程&#xff08;25%&#xff09;系统架构设计&#xff08;35%&#xff09;信…