每日学习一个数据结构-红黑树

news2024/11/14 12:12:59

文章目录

    • 什么是红黑树?
      • 示意图
      • 红黑树的特点
      • 红黑树的节点结构
      • 插入和删除操作
        • 旋转操作
        • 重新着色
      • 红黑树的应用
    • 树的构造过程
      • 插入新节点
        • 自平衡调整策略
      • 示例
    • 查询过程

什么是红黑树?

红黑树(Red-Black Tree)是一种自平衡的二叉查找树(Binary Search Tree, BST)。红黑树的设计目的是为了在插入和删除操作期间保持树的平衡,从而确保操作的时间复杂度为 O(log n),其中 n 是树中的节点数量。这种平衡有助于在最坏情况下也保持良好的性能表现。

示意图

红黑树

红黑树的特点

红黑树具有以下五个性质,这些性质确保了红黑树的自平衡性:

  1. 每个节点要么是红色,要么是黑色
  2. 根节点是黑色
  3. 所有叶子节点(NIL 节点,空节点)都是黑色
  4. 如果一个节点是红色的,则它的两个子节点必须是黑色的(即,红色节点不能连续出现,或者说红色节点的孩子必须是黑色)。
  5. 任意节点到其每个叶子节点的所有路径上包含相同数目的黑色节点(这被称为“黑色高度”)。

红黑树的节点结构

通常情况下,红黑树的节点除了包含关键字、左子树指针、右子树指针和父节点指针之外,还会包含一个颜色属性(红色或黑色)。这里的“红色”和“黑色”并不是真正的颜色,而是逻辑标记,用来帮助算法执行自平衡操作。

插入和删除操作

在红黑树中插入或删除节点时,可能会破坏上述的一些性质。因此,在插入或删除操作之后,需要进行一系列的旋转(rotation)和重新着色(recoloring)操作,以恢复红黑树的性质。

旋转操作

旋转操作分为两种:左旋(Left Rotation)和右旋(Right Rotation)。这两种操作可以改变树的形状,但不会改变树的排序顺序。

  • 左旋:对于一个节点 x,其右孩子 y 成为新的根节点,x 成为 y 的左子树。
  • 右旋:对于一个节点 y,其左孩子 x 成为新的根节点,y 成为 x 的右子树。
重新着色

重新着色是指改变节点的颜色以满足红黑树的性质。

红黑树的应用

红黑树因其优良的性能和相对简单的自平衡机制,在多种场景下得到广泛应用,尤其是在计算机科学领域,如:

  • 关联数组:在 C++ STL 中的 std::map 和 std::set,以及 Java 的 TreeMap 和 TreeSet,都使用了红黑树作为底层实现。
  • 数据库管理系统:一些数据库管理系统使用红黑树来组织索引数据。
  • 操作系统调度器:一些操作系统中的进程调度算法也使用红黑树来维护进程队列。

红黑树的优点在于它能够在 O(log n) 时间内完成查找、插入和删除操作,同时保持较好的空间效率。因此,它成为许多高效数据结构和算法的基础之一。

树的构造过程

红黑树的构造过程通常涉及插入新节点、删除节点以及必要的自平衡调整。构造过程的关键是在插入或删除节点后,保持红黑树的五个性质不变。下面是红黑树插入新节点的具体步骤及其自平衡调整过程。

插入新节点

  1. 插入操作

    • 新节点总是以红色插入。这是因为红色节点意味着其父节点是黑色,这样插入一个红色节点不会立即违反红黑树的性质。
    • 新节点插入到正确的位置,就像在普通的二叉查找树中一样。具体来说,新节点插入到叶节点的位置(此时的叶节点是指树中没有子节点的节点)。
  2. 修复插入后的不平衡

    • 插入新节点后,可能会导致红黑树的性质被破坏。特别是性质 4(红色节点不能有红色子节点)可能会被破坏。因此,需要进行一系列调整来恢复红黑树的性质。
    • 根据插入节点的位置以及其父节点和叔叔节点(如果有)的颜色,采取不同的调整策略。
自平衡调整策略

调整策略主要包括以下几种情况:

  1. Case 1: 新节点是根节点

    • 如果新插入的节点成为了树的根节点,那么只需简单地将新节点的颜色改为黑色即可。
  2. Case 2: 叔叔节点是红色

    • 如果新节点的叔叔节点是红色,那么可以将新节点的父节点和叔叔节点都设为黑色,而祖父节点设为红色。
    • 然后,将祖父节点设为当前节点,继续向上调整。
  3. Case 3: 叔叔节点是黑色(或不存在)

    • 如果新节点的叔叔节点是黑色或不存在(即新节点的父节点是祖父节点的唯一子节点),则需要进行旋转和重新着色操作来恢复红黑树的性质。

    这种情况又分为几种子情况:

    • Case 3a: 新节点是其父节点的右子节点,且父节点是祖父节点的左子节点

      • 执行左旋操作,将新节点的父节点变为当前节点,并进行 Case 3b 的检查。
    • Case 3b: 新节点是其父节点的左子节点,且父节点是祖父节点的左子节点

      • 将新节点的父节点设为黑色,祖父节点设为红色。
      • 对祖父节点执行右旋操作。
    • Case 3c: 新节点是其父节点的左子节点,且父节点是祖父节点的右子节点

      • 执行右旋操作,将新节点的父节点变为当前节点,并进行 Case 3d 的检查。
    • Case 3d: 新节点是其父节点的右子节点,且父节点是祖父节点的右子节点

      • 将新节点的父节点设为黑色,祖父节点设为红色。
      • 对祖父节点执行左旋操作。

示例

假设我们要在一个空的红黑树中依次插入节点 10、20、30 和 40。

  1. 插入节点 10

    • 作为第一个节点,插入后设为黑色。
  2. 插入节点 20

    • 作为右子节点插入,设为红色。
    • 无需调整,因为只有一个节点,没有违反红黑树性质。
  3. 插入节点 30

    • 作为 20 的右子节点插入,设为红色。
    • 此时,20 和 30 都是红色,违反性质 4,需要调整。
    • 由于 10(祖父节点)是黑色,且 20(父节点)的兄弟节点为空,故采用 Case 3b 的处理方式。
    • 将 20 设为黑色,10 设为红色,并对 10 执行右旋。
  4. 插入节点 40

    • 作为 30 的右子节点插入,设为红色。
    • 此时,30 和 40 都是红色,需要调整。
    • 由于 20(祖父节点)是黑色,且 30(父节点)的兄弟节点为空,故采用 Case 3d 的处理方式。
    • 将 30 设为黑色,20 设为红色,并对 20 执行左旋。

通过以上步骤,可以逐步构建出一棵符合红黑树性质的树。每次插入后,都需要检查并调整以确保树的性质保持不变。

查询过程

红黑树是一种自平衡的二叉查找树,它保证了在最坏的情况下,任何查找、插入或删除操作的时间复杂度都是 O(log n)。下面概述了红黑树的查询过程:

  1. 从根节点开始

    • 查询开始于树的根节点。
  2. 比较键值

    • 将要查找的键值与当前节点的键值进行比较。
      • 如果两者相等,则找到了所查找的键值,查询结束。
      • 如果查找的键值小于当前节点的键值,则继续在当前节点的左子树中查找。
      • 如果查找的键值大于当前节点的键值,则继续在当前节点的右子树中查找。
  3. 递归搜索

    • 根据上述比较结果,选择相应的子树,并在该子树中重复步骤2的操作。
  4. 到达叶子节点

    • 如果一直到达叶子节点(即当前节点为空),且没有找到目标键值,则说明该键值不在树中,查询失败。
  5. 返回结果

    • 如果在树中找到了目标键值,返回相应的节点或值。
    • 如果没有找到目标键值,返回一个指示未找到的标志或特定值。

红黑树的查询过程与普通二叉查找树非常相似,但是由于红黑树的自平衡特性,可以确保树的高度始终保持在较低水平,从而提高了最坏情况下的性能。注意,在实际的查询过程中,不会涉及到红黑树的颜色属性,因为查询仅关心键值的比较,而不涉及树的平衡性质。

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

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

相关文章

关于嵌入式硬件需要了解的基础知识

成长路上不孤单😊😊😊😊😊😊 【14后😊///C爱好者😊///持续分享所学😊///如有需要欢迎收藏转发///😊】 今日分享关于嵌入式硬件基础知识的相关内容&#xff…

TCP: Textual-based Class-aware Prompt tuning for Visual-Language Model

文章汇总 存在的问题 原文:具有图像特定知识的图像条件提示符号在提升类嵌入分布方面的能力较差。 个人理解:单纯把"a photo of {class}"这种提示模版作为输入是不利于text encoder学习的 动机 在可学习的提示和每一类的文本知识之间建立…

2024短剧系统开发,付费短剧小程序app源码教程,分销功能讲解搭建上线

短剧系统技术栈 前端:vue3uniapp 后端: php 数据库:mysql 服务器环境: centos7.6 宝塔 php7.4 MySQL5.7 一、短剧系统功能 短剧用户端: 小程序、抖音小程序、快手小程序、APP、 z付宝小程序 系统用户端详细功能&…

Java创建教程!(*  ̄3)(ε ̄ *)

Java 构造函数 Java面向对象设计 - Java构造函数 构造函数是用于在对象创建后立即初始化对象的代码块。 构造函数的结构看起来类似于一个方法。 声明构造函数 构造函数声明的一般语法是 <Modifiers> <Constructor Name>(<parameters list>) throws <…

安卓14剖析SystemUI的ShadeLogger/LogBuffer日志动态控制输出dumpsy机制

背景&#xff1a; 看SystemUI的锁屏相关代码时候发现SystemUI有一个日志打印相关的方法调用&#xff0c;相比于常规的Log.i直接可以logcat查看方式还是比较新颖。 具体日志打印代码如下&#xff1a; 下面就来介绍一下这个ShadeLogger到底是如何打印的。 分析源码&#xff1…

scanf()函数的介绍及基础用法

目录 scanf&#xff08;&#xff09;函数的介绍及基础用法 一&#xff1a;头文件 二&#xff1a;一般用法 三&#xff1a;返回值 1. 正整数的情况&#xff1a; 2. 0 的情况&#xff1a; 3. EOF的情况&#xff1a; 四&#xff1a;说明 scanf&#xff08;&#xff09;函数…

CCF202006_1

问题描述 试题编号&#xff1a;202006-1试题名称&#xff1a;线性分类器时间限制&#xff1a;1.0s内存限制&#xff1a;512.0MB问题描述&#xff1a; 题解&#xff1a; #include<bits/stdc.h>using namespace std; int n, m;struct Node {int x, y;char ch; }node[1010…

9.16日常记录

1.LRU算法 核心思想:LRU算法&#xff08;Least Recently Used&#xff09;是一种常用的缓存淘汰策略&#xff0c;它的核心思想是“如果数据最近被访问过&#xff0c;那么将来被访问的几率也更高”。LRU算法主要用于内存管理和缓存系统。当内存或缓存空间已满&#xff0c;需要腾…

【工具变量】气候适应型试点城市DID(2005-2022年)

数据来源&#xff1a;本数据来源于中国ZF网发布的《关于深化气候适应型城市建设试点的通知》 时间跨度&#xff1a;2005-2022年数据简介&#xff1a;适应型试点城市是指在应对气候变化、提高城市适应能力方面进行先行先试的城市。根据中国ZF网发布的《关于深化气候适应型城市建…

在 Stable Diffusion 1.5 中 Lora, Dreambooth, Textual Inversion的详解指北

Lora, Dreambooth and Textual Inversion 说明 您是否想象过您可爱的宠物与埃菲尔铁塔合影的画面&#xff0c;或者想象过如何生成一张带有您朋友面孔的人工智能图像&#xff1f; 是的&#xff0c;通过稳定扩散技术的微调&#xff0c;这完全是可能的&#xff01; 创建这些场景…

NISP 一级 | 7.2 信息安全风险管理

关注这个证书的其他相关笔记&#xff1a;NISP 一级 —— 考证笔记合集-CSDN博客 0x01&#xff1a;信息安全风险 信息系统不可能达到绝对安全&#xff0c;但可以通过安全风险&#xff08;以下简称“风险”&#xff09;控制来实现符合个人或单位目标的一定程度的安全。信息安全管…

xxl-job、Quartz、power-job、elastic-job对比选型

一、框架对比 1. Quartz 优点&#xff1a;稳定性和可扩展性好&#xff0c;适用于企业级应用&#xff1b;调度功能丰富&#xff0c;满足多种需求。 缺点&#xff1a;本身不提供原生的分布式支持&#xff0c;需要通过扩展或与其他组件结合来实现分布式任务调度&#xff1b;调度…

树莓派驱动之spi回环测试

开启spi sudo raspi-config选择Interfacing options,选择spi打开 lsmod可以看到spi_bcm2835 短接MISO和MOSI 编写回环代码spitest.c #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <string.h>…

第六部分:1---进程间通信,匿名管道

目录 进程间通信 进程间通信的目的&#xff1a; 进程间通信的本质&#xff1a; 管道&#xff1a; 管道的定义&#xff1a; 匿名管道 单向通信的管道通路&#xff1a; 进程和文件之间的解耦&#xff1a; 单向管道的读写端回收问题&#xff1a; 管道通信主要实现动态数…

Python VS Golng 谁更胜一筹?

今天我们聊聊Python和Golang这俩到底谁更胜一筹。 这个话题我已经在各种技术论坛上看到无数次了&#xff0c;每次都能引起一波热烈的讨论。作为一个多年写代码的老程序员&#xff0c;今天就站在我的角度&#xff0c;和大家掰扯掰扯这两个语言各自的优缺点。 1. 性能与并发模型…

283. 移动零(快慢指针)

算法分析&#xff1a; 如果数组没有0&#xff0c;快慢指针同步移动&#xff0c;元素会被自己复制&#xff1b;如果有0&#xff0c;快指针找到非零元素&#xff0c;将其复制到慢指针位置最终将剩余位置填充为0。 代码&#xff1a; class Solution {public void moveZeroes(i…

Android Studio 2024 安装、项目创建、加速、优化

文章目录 Android Studio安装Android Studio项目创建Android Studio加速修改GRADLE_USER_HOME位置减少C盘占用空间GRADLE加速 修改模拟器位置减少C盘占用空间参考资料 Android Studio安装 下载android studio download android-studio-2024.1.2.12-windows.exe 或者 android-…

11 - TCPClient实验

在上一个章节的UDP通信测试中&#xff0c;尽管通信的实现过程相对简洁&#xff0c;但出现了通信数据丢包的问题。因此&#xff0c;本章节将基于之前建立的WIFI网络连接&#xff0c;构建一个基础的TCPClient连接机制。我们利用网络调试助手工具来发送数据&#xff0c;测试网络通…

[PICO VR眼镜]眼动追踪串流Unity开发与使用方法,眼动追踪打包报错问题解决(Eye Tracking/手势跟踪)

前言 最近在做一个工作需要用到PICO4 Enterprise VR头盔里的眼动追踪功能&#xff0c;但是遇到了如下问题&#xff1a; 在Unity里面没法串流调试眼动追踪功能&#xff0c;根本获取不到Device&#xff0c;只能将整个场景build成APK&#xff0c;安装到头盔里&#xff0c;才能在…

【技术解析】消息中间件MQ:从原理到RabbitMQ实战(深入浅出)

文章目录 【技术解析】消息中间件MQ&#xff1a;从原理到RabbitMQ实战(深入浅出)1.简介1.1 什么是消息中间件1.2 传统的http请求存在那些缺点1.3 Mq应用场景有那些1.4 为什么需要使用mq1.5 Mq与多线程之间区别1.6 Mq消息中间件名词1.7主流mq区别对比1.8 Mq设计基础知识 2.Rabbi…