【数据结构 08】红黑树

一、概述

红黑树,是一种二叉搜索树,每一个节点上有一个存储位表示节点的颜色,可以是Red或Black。

通过对任何一条从根到叶子的路径上各个节点着色方式的限制,红黑树确保没有一条路径会比其他路径长上两倍,因而是接进平衡的。

红黑树性质:

  • 根节点是黑色
  • 红节点的两个孩子一定是黑色的;黑节点的两个孩子不一定是红色的。没有连续的红节点
  • 对于每个节点,从该节点到其后所有后代叶节点的简单路径上,均包含相同数目的黑节点
  • 每个叶子节点都是黑色的(NIL空节点)

二、算法

红黑树在设计的时候,插入策略与AVL树一样,只是插入之后的调整策略与AVL不同(旋转策略是一样的,但是红黑树需要考虑变色且无需再考虑平衡因子)

只看遍历的时间复杂度的话,AVL树的时间复杂度是低于红黑树的,因为AVL树的时间复杂度是无限接近于O(\log_2 n),而红黑树的时间复杂度是O(\log_2 n) ~2 * O(\log_2 n),但这在系统层面的时间损失很小。

从调整策略的角度,红黑树的调整次数与旋转次数都远低于AVL树。所以综合来看,红黑树的性能是优于AVL树的,map和set的底层封装的也正是红黑树。

三、调整策略

红黑树的根节点一定是黑色,新插入的节点默认为是红色。

当新插入一个红色节点cur时,先观察cur的父节点parent,如果父节点是黑色,则无需调整;如果父节点也是红节点,那么再观察cur节点的叔叔节点uncle,根据uncle节点的情况进行调整。

红黑树调整策略的核心思路:不能出现连续的红色节点,每条路径的黑色节点数量一样

调整策略分为三种情况:

  • 情况1:父节点parent和叔叔节点uncle都是红色,此时只需变色调整,不需旋转,并向上调整
  • 情况2:父节点parent为红色,叔叔节点不存在或为黑色,cur节点和parent节点都同为左节点或同为右节点,此时需要左单旋或者右单旋,无需向上调整
  • 情况3:父节点parent为红色,叔叔节点不存在或为黑色,cur节点为左节点时parent节点为右节点,或者cur节点为右节点时parent节点为左节点,此时需要左右双旋或者右左双旋,无需向上调整

情况1:parent节点是红色,uncle节点也是红色

调整方法:parent节点与uncle节点变为黑色,祖父节点grandparent节点变为红色,然后将cur变为祖父节点,parent节点依然为cur节点的父节点,向上调整,直到出现parent节点为空,最后再将根节点置为黑色。

如图:

情况1

 

情况1

情况2: 父节点parent为红色,叔叔节点不存在或为黑色,cur节点和parent节点都同为左节点或同为右节点

调整方法:以祖父节点grandparent为轴点进行左单旋或则右单旋,父节点变成黑色,祖父节点变成红色。

如图:

情况2
情况1 情况2 结合调整

情况3:父节点parent为红色,叔叔节点不存在或为黑色,cur节点为左节点时parent节点为右节点,或者cur节点为右节点时parent节点为左节点

调整方法:先以parent节点为轴心进行左单旋或者右单旋,再以grandparent节点为轴心进行与上一步操作相反的单旋,最后将cur节点变成黑色,将grandparent节点变为红色

示例:将数列{ 16, 3, 7, 9, 26, 18, 14, 15, 13, 11 }按顺序插入红黑色中

图1 插入16、3、7
图2 插入9
图3 插入26、18
图4 插入14、15
图5 插入13
图6 插入11

四、RBTree.h

#define _CRT_SECURE_NO_WARNINGS 1#pragma once
#include <iostream>enum Color
{RED,BLACK
};template<class K, class V>
struct RBTreeNode
{std::pair<K, V> kv;RBTreeNode* parent;RBTreeNode* left;RBTreeNode* right;Color col;RBTreeNode(const std::pair<K, V>& x): kv(x), parent(nullptr), left(nullptr), right(nullptr), col(RED){}
};template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public:bool Insert(const std::pair<K, V>& x){if (_root == nullptr){_root = new Node(x);_root->col = BLACK;return true;}// 寻找新节点该插入的位置Node* cur = _root;Node* parent = nullptr;while (cur){parent = cur;if (cur->kv.first > x.first)cur = cur->left;else if (cur->kv.first < x.first)cur = cur->right;elsereturn false;}// 创建新节点cur = new Node(x);cur->parent = parent;if (parent->kv.first > x.first)parent->left = cur;elseparent->right = cur;// 调整颜色while (parent && parent->col == RED){Node* grandpa = parent->parent;if (grandpa->left == parent){Node* uncle = grandpa->right;if (uncle && uncle->col == RED){// 情况1,变色parent->col = uncle->col = BLACK;grandpa->col = RED;cur = grandpa;parent = cur->parent;}else{if (parent->left == cur){// 情况2,右单旋_RotateRight(grandpa);parent->col = BLACK;grandpa->col = RED;}else{// 情况3,左右双旋_RotateLeft(parent);_RotateRight(grandpa);cur->col = BLACK;grandpa->col = RED;}break;}}else{Node* uncle = grandpa->left;if (uncle && uncle->col == RED){// 情况1,变色parent->col = uncle->col = BLACK;grandpa->col = RED;cur = grandpa;parent = cur->parent;}else{if (parent->right == cur){// 情况2,左单旋_RotateLeft(grandpa);parent->col = BLACK;grandpa->col = RED;}else{// 情况3,右左双旋_RotateRight(parent);_RotateLeft(grandpa);cur->col = BLACK;grandpa->col = RED;}break;}}}_root->col = BLACK;return true;}void InOrder(){_InOrder(_root);std::cout << std::endl;}bool IsBalance(){if (_root == nullptr)return true;if (_root->col == RED)return false;// 计算最左路径上的黑节点数量int ref = 0;Node* left = _root;while (left){if (left->col == BLACK)++ref;left = left->left;}return _IsBalance(_root, 0, ref);}private:void _RotateLeft(Node* parent){Node* subR = parent->right;Node* subRL = subR->left;parent->right = subRL;if (subRL)subRL->parent = parent;subR->left = parent;Node* ppNode = parent->parent;parent->parent = subR;if (ppNode == nullptr){_root = subR;subR->parent = nullptr;}else{if (ppNode->left == parent)ppNode->left = subR;elseppNode->right = subR;subR->parent = ppNode;}}void _RotateRight(Node* parent){Node* subL = parent->left;Node* subLR = subL->right;parent->left = subLR;if (subLR)subLR->parent = parent;subL->right = parent;Node* ppNode = parent->parent;parent->parent = subL;if (ppNode == nullptr){_root = subL;subL->parent = nullptr;}else{if (ppNode->left == parent)ppNode->left = subL;elseppNode->right = subL;subL->parent = ppNode;}}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->left);std::cout << "<" << root->kv.first << "," << root->kv.second << "> ";_InOrder(root->right);}bool _IsBalance(Node* root, int blackNum, int ref){if (root == nullptr){if (blackNum != ref){std::cout << "路径黑色节点数量不相等" << std::endl;return false;}return true;}if (root->col == RED && root->parent->col == RED){std::cout << "路径出现连续红节点" << "<" << root->kv.first << "," << root->kv.second << "> " << std::endl;return false;}if (root->col == BLACK)++blackNum;return _IsBalance(root->left, blackNum, ref)&& _IsBalance(root->right, blackNum, ref);}private:Node* _root = nullptr;
};

五、test.cpp

#define _CRT_SECURE_NO_WARNINGS 1#include "RBTree.h"
#include <ctime>void test1_RBTree()
{int arr[] = { 16, 3, 7, 9, 26, 18, 14, 15, 13, 11 };//int arr[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };RBTree<int, int> t;for (auto& e : arr){t.Insert(std::make_pair(e, e));}t.InOrder();std::cout << std::endl;std::cout << t.IsBalance() << std::endl;
}void test2_RBTree()
{RBTree<int, int> t;for (int i = 0; i < 100000; ++i){int x = rand() % 10000;t.Insert(std::make_pair(x, x));}t.InOrder();std::cout << std::endl;std::cout << t.IsBalance() << std::endl;
}int main()
{srand(time(nullptr));test1_RBTree();//test2_RBTree();return 0;
}

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

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

相关文章

UGUI中Text和TextMeshPro实现图文混排方式

一些项目中实现图文混排是自定义一个脚本去继承Text类&#xff0c;然后文本中用富文本的方式进行图片和超链接的定义&#xff0c;在代码中用正则表达式匹配的方式把文本中图片和超链接给替换&#xff0c;如下&#xff1a; TextMeshPro实现是生成SpriteAsset进行图文混排的&…

YOLOv8-Segment C++

YOLOv8-Segment C https://github.com/triple-Mu/YOLOv8-TensorRT 这张图像是运行yolov8-seg程序得到的结果图&#xff0c;首先是检测到了person、bus及skateboard(这个是检测错误&#xff0c;将鞋及其影子检测成了滑板&#xff0c;偶尔存在错误也属正常)&#xff0c;然后用方…

go并发编程-runtime、Channel与Goroutine

1. runtime包 1.1.1. runtime.Gosched() 让出CPU时间片&#xff0c;重新等待安排任务(大概意思就是本来计划的好好的周末出去烧烤&#xff0c;但是你妈让你去相亲,两种情况第一就是你相亲速度非常快&#xff0c;见面就黄不耽误你继续烧烤&#xff0c;第二种情况就是你相亲速度…

电脑用的视频编辑软件有哪些 视频剪辑软件排行榜 视频剪辑软件推荐 视频剪辑培训学习 视频剪辑制作自学 电脑视频剪辑需要什么配置

电脑视频剪辑软件这么多&#xff0c;到底哪些比较好用&#xff1f;下面就让我们以十大电脑视频剪辑软件排行榜来细数好用的软件。另外&#xff0c;电脑视频剪辑需要什么配置&#xff1f;本文也会给大家从内存、CPU等参数上介绍&#xff0c;并推荐好用的电脑设备。 一、十大电脑…

Javaweb之SpringBootWeb案例之配置文件的详细解析

4. 配置文件 员工管理的增删改查功能我们已开发完成&#xff0c;但在我们所开发的程序中还一些小问题&#xff0c;下面我们就来分析一下当前案例中存在的问题以及如何优化解决。 4.1 参数配置化 在我们之前编写的程序中进行文件上传时&#xff0c;需要调用AliOSSUtils工具类&…

基于springboot+vue的校园赛事资讯网站(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

【Python笔记-设计模式】建造者模式

一、说明 又称生成器&#xff0c;是一种创建型设计模式&#xff0c;使其能够分步骤创建复杂对象。允许使用相同的创建代码生成不同类型和形式的对象。 (一) 解决问题 对象的创建问题&#xff1a;当一个对象的构建过程复杂&#xff0c;且部分构建过程相互独立时&#xff0c;可…

leetcode-704.二分查找

题目 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 示例 1: 输入: nums [-1,0,3,5,9,12], target 9输出: 4 解释: 9 …

Web性能优化之如何评估网页性能——性能指标和度量工具介绍

前言 用户在访问 web 网页时&#xff0c;大部分都希望网页能够在一秒完成。事实上&#xff0c;加载时间每多 1 秒&#xff0c;就会流失 7%的用户。如果时间超过 8s 用户就会感到不耐烦、会放弃访问。这也就是著名的 “8秒原则”。 虽然当今设备及网络环境都大幅提升&#xff…

Android 跳转应用设置/热点界面或等常用操作

Android 跳转应用设置/热点界面或等常用操作 https://www.jianshu.com/p/ba7164126690 android学习进阶——Setting https://blog.csdn.net/csdn_wanziooo/article/details/81980984 Android 7.1 以太网反射 EthernetManager 配置 DHCP、静态 IP https://codeleading.com/art…

Java List的合并与切分

在Java开发中经常遇到list结构数据的处理&#xff0c;如List的合并或拆分&#xff0c;记录下来&#xff0c;方便备查。 一、List 合并 两个list数据的合并处理&#xff0c;可使用Java8 新特性的stream流&#xff0c;根据实际需要遍历取值。 1、定义 UserInfo 对象 订单的相…

Request对象-获取请求消息

Request 概述&#xff1a;Request 和 Response 对象都是由 Web 服务器(Tomcat)创建的&#xff0c;我们来使用它们&#xff0c;Request 对象是用来 获取请求消息 的&#xff0c;Response 对象是用来 设置响应消息 的 Request 对象的原理 Request 对象的继承体系结构 Reque…

IS-IS的LSP分片扩展

原理 IS-IS通过泛洪LSP来宣告链路状态信息,由于一个LSP能够承载的信息量有限,IS-IS将对LSP进行分片。每个LSP分片由产生该LSP的结点或伪结点的SystemID、PseudnodeID(普通LSP中该值为0,Pseudonode LSP中该值为非0)、LSPNumber(LSP分片号)组合起来唯一标识,由于LSPNumb…

【大数据安全】数据管理安全安全分析隐私保护

目录 一、数据管理安全 &#xff08;一&#xff09;数据溯源 &#xff08;二&#xff09;数字水印 &#xff08;三&#xff09;策略管理 &#xff08;四&#xff09;完整性保护 &#xff08;五&#xff09;数据脱敏 二、安全分析 &#xff08;一&#xff09;大数据安全…

【昕宝爸爸小模块】日志系列之什么是分布式日志系统

➡️博客首页 https://blog.csdn.net/Java_Yangxiaoyuan 欢迎优秀的你&#x1f44d;点赞、&#x1f5c2;️收藏、加❤️关注哦。 本文章CSDN首发&#xff0c;欢迎转载&#xff0c;要注明出处哦&#xff01; 先感谢优秀的你能认真的看完本文&…

【Linux】环境基础开发工具的使用(一)

前言&#xff1a;在此之前我们学习了一些Linux的权限&#xff0c;今天我们进一步学习Linux下开发工具的使用。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:Linux的深度刨析 &#x1f448; &#x1f4af;代码仓库:卫卫周大胖的学习日记…

关于谷歌新版调试用具(Chrome Dev Tool ),网络选项(chrome-network)默认开启下拉模式的设置

今天在使用谷歌浏览器进行调试的时候&#xff0c;打开调试工具网络选项发现过滤不同模式的选项卡不见了&#xff0c;转而变成一个下拉式选项&#xff0c;如下图 这样一来使得切换不同类型查看的时候变得非常不方便&#xff0c;然后网上查了一下发现这个功能谷歌在很早版本就已…

前后端项目

文章目录 1.需求2.项目搭建2.1项目结构图2.2构建聚合工程2.2.1 zx-parent父工程2.2.2 zx-framework父工程2.2.2.1 zx-common工程2.2.2.2 zx-mybatisplus工程2.2.3 如上,同理创建其他父子工程2.3准备sql3.用户登录3.1 修改pom-依赖其他Module3.2 封装User1.需求 在线学习平台 …

笔记本电脑Win11重装系统教程

在笔记本电脑Win11操作过程中&#xff0c;用户如果遇到很严重的系统问题&#xff0c;就可以重新正常的Win11系统&#xff0c;快速解决Win11系统问题。但是&#xff0c;部分新手用户不知道不知道如何操作才能给Win11笔记本电脑重装系统&#xff1f;以下小编分享笔记本电脑Win11重…

LaTeX教程(003)-LaTeX文档结构(03)

LaTeX教程(003)- LaTeX \LaTeX LATE​X文档结构(03) 2.2 章节划分命令 在前面的内容中&#xff0c;我们讨论了文档的最顶层的区块划分&#xff0c;即front matter、main matter和back matter。这一节中&#xff0c;我们讨论如何在这些区块内部&#xff0c;使用章节划分命令对…