数据结构与算法——19.红黑树

这篇文章我们来讲一下红黑树。

目录

1.概述

1.1红黑树的性质

2.红黑树的实现

3.总结


1.概述

首先,我们来大致了解一下什么是红黑树

红黑树是一种自平衡的二叉查找树,是一种高效的查找树。红黑树具有良好的效率,它可在 O(logN) 时间内完成查找、增加、删除等操作。因此,红黑树在业界应用很广泛,比如 Java 中的 TreeMap,JDK 1.8 中的 HashMap。

1.1红黑树的性质

看过前面二叉查找树(即二叉搜索树)的同学都知道,普通的二叉查找树在极端情况下可退化成链表,此时的增删查效率都会比较低下。

为了避免这种情况,就出现了一些自平衡的查找树,比如 AVL,红黑树等。这些自平衡的查找树通过定义一些性质,将任意节点的左右子树高度差控制在规定范围内,以达到平衡状态。前面讲的AVL树实现自平衡的机制是设置一个平衡因子。

以红黑树为例,红黑树通过如下的性质定义实现自平衡:

  • 所有节点都有两种颜色:红色或黑色。
  • 根节点是黑色。
  • 所有null视为黑色。
  • 每个红色节点必须有两个黑色的子节点,即红色节点不能相邻(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
  • 从根节点到任意一个叶子节点,路径中的黑色节点数目一样(黑色完美平衡,简称黑高)。

有了上面的几个性质作为限制,即可避免二叉查找树退化成单链表的情况。

但是,仅仅避免这种情况还不够,这里还要考虑某个节点到其每个叶子节点路径长度的问题。

如果某些路径长度过长,那么,在对这些路径上的节点进行增删查操作时,效率也会大大降低。

这个时候性质4和性质5用途就凸显了,有了这两个性质作为约束,即可保证任意节点到其每个叶子节点路径最长不会超过最短路径的2倍。原因如下:

当某条路径最短时,这条路径必然都是由黑色节点构成。当某条路径长度最长时,这条路径必然是由红色和黑色节点相间构成(性质4限定了不能出现两个连续的红色节点)。而性质5又限定了从任一节点到其每个叶子节点的所有路径必须包含相同数量的黑色节点。此时,在路径最长的情况下,路径上红色节点数量 = 黑色节点数量。该路径长度为两倍黑色节点数量,也就是最短路径长度的2倍。

下面看几个红黑树:

2.红黑树的实现

下面来看一下红黑树的实现

package Tree;
/**红黑树的相关操作*/
public class L4_RBTree {enum Color{RED,BLACK}private Node root;private static class Node {int key;Object value;Node left;Node right;Node parent;//父节点Color color = Color.RED;//颜色public Node(int key, Object value) {this.key = key;this.value = value;}//是否是左孩子boolean isLeftChild(){return parent != null && parent.left == this;}//找叔叔结点Node isUncle(){if (parent == null || parent.parent == null){return null;}if (parent.isLeftChild()){return parent.parent.right;}else {return parent.parent.left;}}//找兄弟Node sibling(){if (parent == null){return null;}if (this.isLeftChild()){return parent.right;}else {return parent.left;}}}//判断是不是红色boolean isRed(Node node){return node != null && node.color == Color.RED;}//判断是不是黑色boolean isBlack(Node node){return  !isRed(node);}/*** 右旋* 要对parent进行处理* 选转后新根的父子关系* */private void rightRotate(Node node){Node nodeParent = node.parent;Node nodeLeft = node.left;Node nodeLeftRight = nodeLeft.right;if (nodeLeftRight != null){nodeLeftRight.parent = node;}nodeLeft.right = node;nodeLeft.parent = nodeParent;node.left = nodeLeftRight;node.parent = nodeLeft;if (nodeParent == null){root = nodeLeft;}else if (nodeParent.left == node){nodeParent.left = nodeLeft;}else {nodeParent.right = nodeLeft;}}//左旋private void leftRotate(Node node){Node nodeParent = node.parent;Node nodeRight = node.right;Node nodeRightLeft = nodeRight.left;if (nodeRightLeft != null){nodeRightLeft.parent = node;}nodeRight.left = node;nodeRight.parent = nodeParent;node.right = nodeRightLeft;node.parent = nodeRight;if (nodeParent == null){root = nodeRight;}else if (nodeParent.left == node){nodeParent.left = nodeRight;}else {nodeParent.right = nodeRight;}}/**新增或更新*/public void put(int key,Object value){Node p = root;Node parent = null;while (p != null){if (key < p.key){p = p.left;} else if(p.key < key){p = p.right;} else {p.value = value;return;}}Node inserted = new Node(key,value);if (parent == null){root = inserted;}else if (key < parent.key){parent.left = inserted;inserted.parent = parent;}else {parent.right = inserted;inserted.parent = parent;}fixRedRed(inserted);}/**节点调整*/private void fixRedRed(Node x){//插入节点是跟节点if (x == root){x.color = Color.BLACK;return;}//插入节点的父亲是黑色if (isBlack(x.parent)){return;}//红红相邻且叔叔为红Node parent = x.parent;Node uncle = x.isUncle();Node grandparent = parent.parent;if (isRed(uncle)){parent.color = Color.BLACK;uncle.color = Color.BLACK;grandparent.color = Color.RED;fixRedRed(grandparent);return;}//红红相邻且叔叔为黑if(parent.isLeftChild() && x.isLeftChild()){//LLparent.color = Color.BLACK;grandparent.color = Color.RED;rightRotate(grandparent);}else if (parent.isLeftChild() && !x.isLeftChild()){//LRleftRotate(parent);x.color = Color.BLACK;grandparent.color = Color.RED;rightRotate(grandparent);} else if (!parent.isLeftChild() && !x.isLeftChild()){//RRparent.color = Color.BLACK;grandparent.color = Color.RED;leftRotate(grandparent);}else {//RLrightRotate(parent);x.color = Color.BLACK;grandparent.color = Color.RED;leftRotate(grandparent);}}/**删除*/public void remove(int key){Node deleted = find(key);if (deleted == null){return;}doRemove(deleted);}private void doRemove(Node deleted){Node replaced = findReplaced(deleted);Node parent = deleted.parent;if (replaced == null){//没有孩子//删除的是根节点if (deleted == root){root = null;}else {if(isBlack(deleted)){//复杂调整fixDoubleBlack(deleted);}else {// 无需处理}if (deleted.isLeftChild()){parent.left = null;}else {parent.right = null;}deleted.parent = null;}return;}//有一个孩子if (deleted.left == null || deleted.right == null){//删除的是根节点if (deleted == null){root.key = replaced.key;root.value = replaced.value;root.left = root.right = null;}else {if (deleted.isLeftChild()){parent.left = replaced;}else {parent.right = replaced;}replaced.parent = parent;deleted.left = deleted.right = deleted.parent = null;//有助于垃圾回收if (isBlack(deleted) && isBlack(replaced)){//复杂处理fixDoubleBlack(replaced);}else {replaced.color = Color.BLACK;}}return;}//有两个孩子(是一种转换的操作)int t = deleted.key;deleted.key = replaced.key;replaced.key = t;Object v = deleted.value;deleted.value = replaced.value;replaced.value = v;doRemove(replaced);}/**遇到双黑的不平衡的复杂处理* x表示待调整的结点* */private void fixDoubleBlack(Node x){if (x == root){return;}Node parent = x.parent;Node sibling = x.sibling();//被调整节点的兄弟节点为红色if (isRed(sibling)){if (x.isLeftChild()){leftRotate(parent);}else {rightRotate(parent);}parent.color = Color.RED;sibling.color = Color.BLACK;fixDoubleBlack(x);return;}//兄弟是黑色且两个侄子都是黑色if (sibling != null){if (isBlack(sibling.left) && isBlack(sibling.right)){sibling.color = Color.RED;if (isRed(parent)){parent.color = Color.BLACK;}else {fixDoubleBlack(parent);}}//兄弟是黑色但是侄子有红色else {//LLif(sibling.isLeftChild() && isRed(sibling.left)){rightRotate(parent);sibling.left.color = Color.BLACK;sibling.color = parent.color;parent.color = Color.BLACK;}//LRelse if (sibling.isLeftChild() && isRed(sibling.right)){sibling.right.color = parent.color;leftRotate(sibling);rightRotate(parent);parent.color = Color.BLACK;}//RLelse if (!sibling.isLeftChild() && isRed(sibling.left)){sibling.left.color = parent.color;rightRotate(sibling);leftRotate(parent);parent.color = Color.BLACK;}//RRelse {leftRotate(parent);sibling.right.color = Color.BLACK;sibling.color = parent.color;parent.color = Color.BLACK;}}}else {fixDoubleBlack(parent);}}/**找到要删除的结点*/private Node find(int key){Node p = root;while (p != null){if (key < p.key){p = p.left;}else if (key > p.key){p = p.right;}else {return p;}}return null;}/**查找剩余结点*/private Node findReplaced(Node deleted){if (deleted.left == null && deleted.right == null){return null;}else if (deleted.left == null){return deleted.right;}else if(deleted.right == null){return deleted.left;}Node s = deleted.right;while (s.left != null){s = s.left;}return s;}
}

3.总结

有一说一,这红黑树确实比前面的几种树要难一点,主要是它的属性太多,限制太多。说实话,这篇文章中仅仅只是简单的介绍了一下红黑树,实现了一下红黑树的相关操作,但是红黑树的增加和删除中的一些操作没有细讲,这个有时间了我后面会单独再出一篇红黑树的补充文章然后细讲,这篇就这样了吧。

最后说一点,对于这种类似于链式的结构(实际是树形结构),我们要掌握它的定义,条件,然后画图,然后对照图来进行相关的操作,然后再用代码去实现,这样好一点,而不是凭空想象着去写,那样是写不出来的。

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

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

相关文章

解决u盘在我的电脑中重复显示两个

删除注册表&#xff1a; [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\DelegateFolders\{F5FB2C77-0E2F-4A16-A381-3E560C68BC83}]

Maven下载源码出现:Cannot download sources Sources not found for org.springframwork...

Maven下载源码出现&#xff1a;Cannot download sources Sources not found for org.springframwork… 最近重装了IDEA再次查看源码时发现总是报错&#xff0c;网上找了很多&#xff0c;发现解决方法都是在项目终端执行如下命令&#xff1a; mvn dependency:resolve -Dclassi…

基于MFC和OpenCV实现人脸识别

基于MFC和OpenCV实现人脸识别 文章目录 基于MFC和OpenCV实现人脸识别1. 项目说明1. 创建项目2. 启动窗口3. 登录窗口-添加窗口、从启动窗口跳转4. 启动窗口-美化按钮5. 登录窗口-美化按钮、雪花视频6. 注册窗口-美化按钮、雪花视频、从启动窗口跳转7. 注册窗口-开启摄像头8. 注…

PE文件之导入表

1. 导入表 2. 显示导入表信息的例子 ; 作用: 将RVA地址转成FOA即文件偏移 ; 参数: _pFileHdr 指向读到内存中文件的基址指针 ; _dwRVA 目标RVA地址 ; 返回: 目标RVA转成文件偏移的值 RVA2FOA PROC USES esi edi edx, _pFileHdr:PTR BYTE, _dwRVA:DWORDmov esi, _pFil…

栈的应用场景(二)

有效的括号匹配 1.题目2.图分析3.代码实现 1.题目 2.图分析 3.代码实现 class Solution {public boolean isValid(String s) {//创建一个栈,来放左括号.Stack<Character> stack new Stack<>();//遍历字符串,左括号放进栈for(int i 0 ; i < s.length(); i){ch…

阿里云 Oss 权限控制

前言 最近公司的私有 Oss 服务满了&#xff0c;且 Oss 地址需要设置权限&#xff0c;只有当前系统的登录用户才能访问 Oss 下载地址。一开始想着用 Nginx 做个转发来着&#xff0c;Nginx 每当检测当前请求包含特定的 Oss 地址就转发到我们的统一鉴权接口上去&#xff0c;但是紧…

SpringCloudGateway实现数字签名与URL动态加密

文章目录 对称加密非对称加密什么是数字签名HTTPS与CA⭐Gateway网关的过滤器链如何对自己的路径传输设定一个数字签名&#xff1f;前端获取RSA公钥发送加密后对称密钥后端接收当前会话对称密钥并保存前端发送AES加密请求验证请求 如何实现URL的动态加密&#xff1f; 再网络传递…

基于Python3搭建qt开发环境

Python可视化编程相信大部分刚接触都是tkinter&#xff0c;tkinter是Python自带的库&#xff0c;不需要安装第三方库即可使用&#xff0c;在我的Python专栏中也有很多基于tkinter来设计的可视化界面。本篇文章将尝试另外一个Python的可视化编程库(pyqt)&#xff0c;与tkinter编…

深度学习 二:COVID 19 Cases Prediction (Regression)

Deep Learning 1. 回归算法思路2. 代码2.1 基础操作2.2 定义相关函数2.3.1 定义图像绘制函数2.3.2 数据集加载及预处理2.3.3 构造数据加载器2.3.4 构建前馈神经网络&#xff08;Feedforward Neural Network&#xff09;模型2.3.5 神经网络的训练过程2.3.6 模型评估2.3.7 模型测…

【考研数学】高等数学第七模块 —— 曲线积分与曲面积分 | 3. 对面积的曲面积分(第一类曲面积分)

文章目录 二、曲面积分2.1 对面积的曲面积分&#xff08;第一类曲面积分&#xff09;2.1.1 问题引入 —— 曲面的质量2.1.2 对面积的曲面积分定义及性质2.1.3 对面积的曲面积分的计算法 写在最后 二、曲面积分 2.1 对面积的曲面积分&#xff08;第一类曲面积分&#xff09; 2…

YTM32的电源管理与低功耗系统详解

YTM32的电源管理与低功耗系统详解 苏勇&#xff0c;2023年10月 文章目录 YTM32的电源管理与低功耗系统详解缘起原理与机制电源管理模型的功耗模式正常模式&#xff08;Normal&#xff09;休眠模式&#xff08;Sleep&#xff09;深度休眠模式&#xff08;DeepSleep&#xff09;…

Flutter+SpringBoot实现ChatGPT流实输出

FlutterSpringBoot实现ChatGPT流式输出、上下文了连续对话 最终实现Flutter的流式输出上下文连续对话。 这里就是提供一个简单版的工具类和使用案例&#xff0c;此处页面仅参考。 服务端 这里直接封装提供工具类&#xff0c;修改自己的apiKey即可使用&#xff0c;支持连续…

Python使用词云图展示

网上看到一个txt文本信息&#xff0c;共2351条饭否记录&#xff0c;据说是微信之父每天发的饭否记录&#xff0c;其实我不知道什么是饭否。我读取这个文本内容&#xff0c;展示到词语图上。之前也使用过&#xff0c;但是好久没有玩Python了&#xff0c;称假期空闲&#xff0c;练…

Spring的注解开发-注解方式整合MyBatis代码实现

之前使用xml方式整合了MyBatis&#xff0c;文章导航&#xff1a;Spring整合第三方框架-MyBatis整合Spring实现-CSDN博客 现在使用注解的方式无非是就是将xml标签替换为注解&#xff0c;将xml配置文件替换为配置类而已。 非自定义配置类 package com.example.Configure;import c…

黑马头条项目环境搭建

注册中心网关配置 spring:cloud:gateway:globalcors:add-to-simple-url-handler-mapping: truecorsConfigurations:[/**]:allowedHeaders: "*"allowedOrigins: "*"allowedMethods:- GET- POST- DELETE- PUT- OPTIONroutes:# 平台管理- id: useruri: lb://…

c# 委托 事件 lambda表达式

委托 C/C中的函数指针实例&#xff1a; typedef int (*Calc)(int a, int b); //这里必须加括号 int Add(int a, int b) {return a b; } int Sub(int a, int b) {return a - b; } int main() {int x 100;int y 200;int z 0;Calc funcPoint1 &Add;Calc funcPoint2 &am…

Android学习之路(19) ListView详解

一.ListView简介 在Android开发中&#xff0c;ListView是一个比较常用的控件。它以列表的形式 展示具体数据内容&#xff0c;并且能够根据数据的长度自适应屏幕显示。 二.ListView简单用法 代码部分 1.布局界面 activity_main.xml 代码&#xff1a; <?xml version"…

新手学习笔记-----编译和链接

目录 1. 翻译环境和运⾏环境 2. 翻译环境&#xff1a;预编译编译汇编链接 2.1 预处理 2.2 编译 2.2.1 词法分析 2.2.2 语法分析 2.2.3 语义分析 2.3 汇编 2.4 链接 3. 运⾏环境 1. 翻译环境和运⾏环境 在ANSI C的任何⼀种实现中&#xff0c;存在两个不同的环境。 第…

LSTM+CRF模型

今天讲讲LSTM和CRF模型&#xff0c;LSTM&#xff08;长短期记忆&#xff09;是一种特殊的循环神经网络&#xff08;RNN&#xff09;模型&#xff0c;用于处理序列数据、时间序列数据和文本数据等。LSTM通过引入门控机制&#xff0c;解决了传统RNN模型在处理长期依赖关系时的困难…

win10 关闭病毒防护

windows10彻底关闭Windows Defender的4种方法 - 知乎