数据结构——红黑树

文章目录

  • 一.红黑树的定义
  • 二.红黑树的插入
    • 1.红黑树节点的定义
    • 2.红黑树的插入操作
    • 3.总结:
  • 三.红黑树与AVL树的比较
  • 四.检验手写的红黑树
  • 五.源码

一.红黑树的定义

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

在这里插入图片描述

红黑树的性质:

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

解读:

性质三:保证树中没有连续的红色节点

性质四:每条路径上黑色节点的数目相同

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

其中,其极限最短:全黑。极限最长:一黑一红……

可以发现,最坏情况的时间复杂度和AVL树一样,都是O(logN),但是红黑树这种近似平衡的结构减少了大量旋转,综合性能优于AVL树。

二.红黑树的插入

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():_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv){}
};

2.红黑树的插入操作

插入步骤:

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

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

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

    • 情况一: cur为红,p为红,g为黑,u存在且为红
      在这里插入图片描述
      解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整

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

    解决方式:pg的左孩子,curp的左孩子,则进行右单旋转;相反,pg的右孩子,curp的右孩子,则进行左单旋转。p、g变色–p变黑,g变红

    • 情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑
      在这里插入图片描述
      解决方式:pg的左孩子,curp的右孩子,则针对p做左单旋转;相反,pg的右孩子,curp的左孩子,则针对p做右单旋转

3.总结:

插入新节点时,父节点为红,看叔叔的颜色。

  1. 叔叔存在且为红,变色,向上调整(可能变为三种情况中的任意一种)
  2. 叔叔不存在/存在且为黑,直线。单旋+变色
  3. 叔叔不存在/存在且为黑,折线,两次单旋+变色

三.红黑树与AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O( l o g 2 N log_2 N log2N)

红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

四.检验手写的红黑树

//检查有没有连续红节点
bool Check(Node* root,int blackNum,const int ref)
{if (root == nullptr){if (blackNum != ref){cout << "路径上黑节点数量不一致" << endl;return false;}return true;}if (root->_col == BLACK){++blackNum;}if (root->_col == RED && root->_parent->_col == RED){cout << "违反规则,父子均为红" << endl;return false;}return Check(root->_left, blackNum,ref) && Check(root->_right, blackNum, ref);
}
//统计某条路径的黑色节点数量,然后计算所有路径的黑色节点数量与该路径比对
bool _IsBalance()
{if (_root == nullptr)return true;if (_root->_col != BLACK){return false;}int ref = 0;Node* left = _root;while (left != nullptr){if (left->_col == BLACK){++ref;}left = left->_left;}return Check(_root,0,ref);
}

五.源码

namespace dianxia
{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){}};template<class K,class V>class RBTree{typedef RBTreeNode<K,V> Node;public: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);cur->_col = Red;//连接节点if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;//检查是否满足红黑树的要求while (parent && parent->_col == Red){//p为红则gp一定存在且为黑Node* grandparent = parent->_parent;assert(grandparent);assert(grandparent->_col==Black);if (parent == grandparent->_left){Node* uncle = grandparent->_right;//1.u存在且为红,变色并继续向上更新if (uncle && uncle->_col == Red){parent->_col = uncle->_col = Black;grandparent->_col = Red;cur = grandparent;parent = cur->_parent;}//2.u不存在或u为黑  变色加旋转else{if (cur == parent->_left){//	    g//    p   u//  c//右旋RotateR(grandparent);parent->_col = Black;grandparent->_col = Red;}else{//		g//	  p	  u//		c//先左旋再右旋RotateL(parent);RotateR(grandparent);cur->_col = Black;grandparent->_col = Red;}break;}}else //grandparent->_right==parent{Node* uncle = grandparent->_left;//1.u存在且为红,变色并继续向上更新if (uncle && uncle->_col == Red){parent->_col = uncle->_col = Black;grandparent->_col = Red;cur = grandparent;parent = cur->_parent;}//2.u不存在或u为黑  变色加旋转else{if (cur == parent->_right){//	    g//    u   p//		    c//左RotateL(grandparent);parent->_col = Black;grandparent->_col = Red;}else{//		g//	  u	  p//		c//先右旋再左旋RotateR(parent);RotateL(grandparent);cur->_col = Black;grandparent->_col = Red;}break;}}}_root->_col = Black;return true;}~RBTree(){_Destroy(_root);_root = nullptr;}void Inorder(){_InOrder(_root);}//检查红黑树:1.检查以根节点为根的每条路径黑色节点的数目是否都相等//			2.检查某条路径上是否有连续的红色节点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 _Check(_root, 0, benchmark);}int Height(){return _Height(_root);}private:void _Destroy(Node* root){if (root == nullptr){return;}_Destroy(root->_left);_Destroy(root->_right);delete root;}int _Height(Node* root){if (root == nullptr)return 0;int leftH = _Height(root->_left);int rightH = _Height(root->_right);return leftH > rightH ? leftH + 1 : rightH + 1;}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << " ";_InOrder(root->_right);}bool _Check(Node* root, int blacknum, int benchmark){if (root == nullptr){if (benchmark != blacknum){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 _Check(root->_left, blacknum, benchmark)&& _Check(root->_right, blacknum, benchmark);}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* ppnode = parent->_parent;subR->_left = parent;parent->_parent = subR;if (ppnode == nullptr){_root = subR;_root->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subR;}else{ppnode->_right = subR;}subR->_parent = ppnode;}}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* ppnode = parent->_parent;subL->_right = parent;parent->_parent = subL;if (parent == _root){_root = subL;_root->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}}private:Node* _root = nullptr;};
}

本文到此结束,码文不易,还请多多支持!!

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

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

相关文章

Typescript+React入门

初识Typescript 出现背景 Typescript&#xff08;以下简称TS&#xff09;实际上就是JavaScriptType&#xff0c;用数据类型的方式来约束了JS的变量定义 在JS的基础上增加了类型支持 在JS中大多数错误都是因为数据类型造成的&#xff0c;所以TS为了规避这个问题加入了类型限制…

【小沐学NLP】在线AI绘画网站(网易云课堂:AI绘画工坊)

文章目录 1、简介1.1 参与方式1.2 模型简介 2、使用费用3、操作步骤3.1 选择模型3.2 输入提示词3.3 调整参数3.4 图片生成 4、测试例子4.1 小狗4.2 蜘蛛侠4.3 人物4.4 龙猫 结语 1、简介 Stable Diffusion是一种强大的图像生成AI&#xff0c;它可以根据输入的文字描述词&#…

从特斯拉FSD v11.4.6,看FSD入华

从特斯拉FSD v11.4.6&#xff0c;看FSD入华 1. 芝加哥城区a. 亮点b. 问题 2. 小镇中心a. 亮点b. 问题 3. FSD入华a. 技术路线b. 场景 4. 参考视频 FSD最近更新了v11.4.6&#xff0c;本文根据2个FSD城区测试视频&#xff0c;一起看一下有哪些亮点和问题。 FSD入华的消息也甚嚣尘…

CentOS7安装Maven详细教程

&#x1f60a; 作者&#xff1a; Eric &#x1f496; 主页&#xff1a; https://blog.csdn.net/weixin_47316183?typeblog &#x1f389; 主题&#xff1a;CentOS7安装Maven详细教程 ⏱️ 创作时间&#xff1a; 2023年08月06日 第一步&#xff1a;上传或下载安装包&#x…

python 输入oracle sql查询语句导出excel表

Author: liukai 2810248865qq.com Date: 2022-08-18 04:28:52 LastEditors: tkhywang 2810248865qq.com LastEditTime: 2023-08-02 18:27:08 FilePath: \PythonProject02\python 连接oracle数据库导出Excel带数据库表头.py Description: 这是默认设置,请设置customMade, 打开ko…

DevOps系列文章之 Docker 安装 NFS 服务器

Docker 安装 NFS 服务器 环境&#xff1a; 192.186.2.105 NFS 服务器 192.168.2.106 Client 客户端 安装 一、服务器端 https://github.com/f-u-z-z-l-e/docker-nfs-server 1、创建目录 mkdir /nfsdata mkdir -p /docker/nfs/2、启动脚本 vim start.sh# 内容 docker run …

Android平台GB28181设备接入端如何降低资源占用和性能消耗

背景 我们在做GB28181设备接入模块的时候&#xff0c;考虑到好多设备性能一般&#xff0c;我们一般的设计思路是&#xff0c;先注册设备到平台侧&#xff0c;平台侧发calalog过来&#xff0c;获取设备信息&#xff0c;然后&#xff0c;设备侧和国标平台侧维持心跳&#xff0c;…

百度秋招攻略,百度网申笔试面试详解

百度秋招简介 作为行业巨头&#xff0c;百度向社会提供的岗位一直都是非常吃香的&#xff0c;每年也都有很多考生密切关注&#xff0c;百度发布的招聘广告&#xff0c;以尽可能的让自己进入这家企业工作&#xff0c;实现自己的人生价值。那么百度每年的秋招时间是多久&#xf…

全面了解CPU Profiler:解读CPU性能分析工具的核心功能与用法

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、概览三、使用3.1 通过调用系统API3.2 通过Android Stu…

[国产MCU]-BL602开发实例-开发环境搭建

开发环境搭建 文章目录 开发环境搭建1、BL602介绍2、软件准备3、源码编译3.1 编译内置工程3.2 自定义工程、自定义组件添加与编译4、固件下载BL602 是一款Wi-Fi + BLE组合的芯片组,用于低功耗和高性能应用开发。无线子系统包含2.4G无线电,Wi-Fi 802.11b/g/n和BLE 5.0 基带/MA…

数组相关练习

数组练习 将数组转化成字符串数组拷贝求数组元素的平均值查找数组中指定元素(顺序查找)二分查找冒泡排序数组逆序 将数组转化成字符串 import java.util.Arrays;public class Text1 {public static void main(String[] args) {int[] arr {5, 6, 4, 2};System.out.println(Arr…

HarmonyOS 开发基础(五)对用户名做点啥

一、实现用户名检验 条件渲染 、生命周期 1.规定用户名长度 2.限定使用的数字及字母&#xff08;涉及正则表达&#xff09; // 导出方式直接从文件夹 import MyInput from "../common/commons/myInput" Entry Component /* 组件可以基于struct实现&#xff0c;组件…