【红黑树】到底是什么,它有哪些基本操作,它的用处是什么,代码如何实现

news2024/11/20 15:18:37

红黑树是一种自平衡二叉查找树,具有良好的时间复杂度和空间复杂度,被广泛应用于计算机科学领域中,如操作系统、编译器、数据库等。在实际应用中,红黑树主要用于实现高效的查找和排序,如 Linux 内核中的进程调度和空闲内存的管理,C++ STL库中的 map 和 set 容器等均使用了红黑树实现。下面将介绍红黑树的特点、基本概念、插入操作、删除操作以及与平衡二叉树和二叉搜索树的区别和联系,同时提供C++代码实现。

本文目录

  • 1. 红黑树的特点(性质)
  • 2. 基本概念
  • 3. 插入操作
  • 4. 删除操作
  • 5. 与平衡二叉树和二叉搜索树的区别和联系
  • 6. C++ 代码实现

1. 红黑树的特点(性质)

在这里插入图片描述

  • 根节点是黑色的;
  • 每个叶子节点都是黑色的空节点(NIL节点);
  • 每个红色节点的两个子节点都是黑色的;
  • 任意一条从根节点到叶子节点的路径上,不能出现连续的两个红色节点;
  • 任意节点到其每个叶子节点的所有路径包含相同数目的黑色节点。

红黑树保证了树的高度(即从根节点到叶子节点的最长路径)最长不超过最短路径的两倍,因而保证了其查找、插入、删除操作的时间复杂度都可以稳定地达到 O(log n) 的级别。

2. 基本概念

  • 黑色节点(B):颜色为黑色的节点。
  • 红色节点(R):颜色为红色的节点。
  • 空节点(NIL):通常用于代表叶子节点,其颜色为黑色。
  • 黑高度(bh):从某个节点到其叶子节点的任意一条路径上的黑色节点数目,空节点的黑高度为0。
  • 平衡:红黑树的节点个数的增减都不会使树的高度增加很多。

3. 插入操作

红黑树的插入操作分为以下几步:

  1. 将新节点插入到红黑树中,颜色为红色。
  2. 如果插入节点的父节点为黑色,则插入完成。
  3. 如果插入节点的父节点为红色,则需要进行调整,以恢复红黑树的平衡。
(a)将插入节点的父节点称为节点P,节点P的父节点称为节点G,节点P的兄弟节点称为节点U;

(b)如果节点G是树的根节点,则置节点G为黑色;

(c)如果节点U是红色,则将节点P和节点U都置为黑色,将节点G置为红色,再以节点G为当前节点进行平衡处理;

(d)如果节点U是黑色,且插入节点为节点P的右节点,即P为G的左孩子,则以P为当前节点进行左旋;

(e)如果节点U是黑色,且插入节点为节点P的左节点,即P为G的右孩子,则以P的左子节点为支点进行右旋;

(f)将节点P置为黑色,将节点G置为红色,以节点G为当前节点进行平衡处理。

4. 删除操作

红黑树的删除操作分为以下几步:

  1. 从红黑树中删除要删除的节点。
  2. 如果删除节点的颜色为红色,则直接删除即可,不需要进行调整操作,红黑树的平衡依然成立。
  3. 如果删除节点的颜色为黑色,则需要进行调整,以恢复红黑树的平衡。
(a)如果删除节点的兄弟节点W是红色的,则以删除节点的父节点P为支点进行左旋或右旋,将W置为黑色,P置为红色,再以W的原来的兄弟节点为新的W节点进行处理;

(b)如果删除节点的兄弟节点W是黑色的,则需要判断W节点的两个子节点的颜色:如果W的两个子节点都是黑色的,则将W节点置为红色,并以P节点为新的删除节点进行平衡处理;如果W的左节点是红色的,右节点是黑色的,则以W节点为支点进行右旋,将W的左节点置为黑色,W置为红色,再以P节点为新的删除节点进行平衡处理;如果W的右节点是红色的,则以P节点为支点进行左旋,将W节点置为红色,W的右节点置为黑色,再以P节点的父节点为新的删除节点进行平衡处理。

5. 与平衡二叉树和二叉搜索树的区别和联系

红黑树、平衡二叉树和二叉搜索树都是非常常见并且重要的数据结构,它们之间有如下的区别和联系:

区别

  • 二叉搜索树(BST):左子树的所有节点值小于根节点,右子树的所有节点值大于根节点,且左右子树也都是 BST。
  • 平衡二叉树:是一种特殊的 BST,即使在最坏情况下,其高度近似于 log N,从而使得查找和删除等操作的时间复杂度可控,通常通过平衡因子进行平衡(如 AVL 树)。
  • 红黑树:是一种二叉搜索树,通过插入、删除等操作来保证树的平衡性,通过约束节点为红或黑色的颜色,从而保证了其近似于平衡。

联系

  • 平衡二叉树和红黑树都是为了保证二叉搜索树的平衡性而引入的改进方法,目的是尽可能保证树的高度较小,以保证时间复杂度的可控性。
  • 红黑树可以看做是一种弱化的平衡二叉树,也就是说,红黑树的平衡条件比 AVL 树等要宽松一些,这就使得其在实际应用中更加灵活。

因为其平均时间复杂度为 O(log N),二叉搜索树、平衡二叉树和红黑树都被广泛应用于搜索、排序、存储和索引等领域。需要根据具体的应用场景和实际情况选择不同的数据结构。

6. C++ 代码实现

下面是使用 C++ 语言实现红黑树的代码,包括节点的定义、查找、插入和删除等操作。

enum Color {RED, BLACK};

template <typename Key, typename Value>
class RBTree {
private:
    struct Node {
        Key key;
        Value value;
        Color color;
        Node *left, *right, *parent;

        Node(Key key, Value value)
            : key(key), value(value), color(RED), left(nullptr), right(nullptr), parent(nullptr) {}
    };

    Node* root;

    // 查找节点
    Node* findNode(Key key, Node* node) {
        if (node == nullptr || node->key == key) {
            return node;
        } else if (node->key < key) {
            return findNode(key, node->right);
        } else {
            return findNode(key, node->left);
        }
    }

    // 左旋
    void leftRotate(Node* x) {
        Node *y = x->right;
        x->right = y->left;
        if (y->left != nullptr) {
            y->left->parent = x;
        }
        y->parent = x->parent;
        if (x->parent == nullptr) {
            root = y;
        } else if (x == x->parent->left) {
            x->parent->left = y;
        } else {
            x->parent->right = y;
        }
        y->left = x;
        x->parent = y;
    }

    // 右旋
    void rightRotate(Node* x) {
        Node *y = x->left;
        x->left = y->right;
        if (y->right != nullptr) {
            y->right->parent = x;
        }
        y->parent = x->parent;
        if (x->parent == nullptr) {
            root = y;
        } else if (x == x->parent->left) {
            x->parent->left = y;
        } else {
            x->parent->right = y;
        }
        y->right = x;
        x->parent = y;
    }

    // 插入节点
    void insertNode(Key key, Value value) {
        Node *z = new Node(key, value);
        Node *y = nullptr;
        Node *x = root;
        while (x != nullptr) {
            y = x;
            if (z->key < x->key) {
                x = x->left;
            } else {
                x = x->right;
            }
        }
        z->parent = y;
        if (y == nullptr) {
            root = z;
        } else if (z->key < y->key) {
            y->left = z;
        } else {
            y->right = z;
        }
        z->left = nullptr;
        z->right = nullptr;
        z->color = RED;
        insertFixup(z);
    }

    // 插入修复
    void insertFixup(Node *z) {
        while (z->parent != nullptr && z->parent->color == RED) {
            if (z->parent == z->parent->parent->left) {
                Node *y = z->parent->parent->right;
                if (y != nullptr && y->color == RED) {
                    z->parent->color = BLACK;
                    y->color = BLACK;
                    z->parent->parent->color = RED;
                    z = z->parent->parent;
                } else {
                    if (z == z->parent->right) {
                        z = z->parent;
                        leftRotate(z);
                    }
                    z->parent->color = BLACK;
                    z->parent->parent->color = RED;
                    rightRotate(z->parent->parent);
                }
            } else {
                Node *y = z->parent->parent->left;
                if (y != nullptr && y->color == RED) {
                    z->parent->color = BLACK;
                    y->color = BLACK;
                    z->parent->parent->color = RED;
                    z = z->parent->parent;
                } else {
                    if (z == z->parent->left) {
                        z = z->parent;
                        rightRotate(z);
                    }
                    z->parent->color = BLACK;
                    z->parent->parent->color = RED;
                    leftRotate(z->parent->parent);
                }
            }
        }
        root->color = BLACK;
    }

    // 删除节点
    void deleteNode(Key key) {
        Node *z = findNode(key, root);
        if (z == nullptr) {
            return;
        }
        Node *y = nullptr;
        if (z->left == nullptr || z->right == nullptr) {
            y = z;
        } else {
            y = findNode(key + 1, z->right);
        }
        Node *x = nullptr;
        if (y->left != nullptr) {
            x = y->left;
        } else {
            x = y->right;
        }
        if (x != nullptr) {
            x->parent = y->parent;
        }
        if (y->parent == nullptr) {
            root = x;
        } else if (y == y->parent->left) {
            y->parent->left = x;
        } else {
            y->parent->right = x;
        }
        if (y != z) {
            z->key = y->key;
            z->value = y->value;
        }
        if (y->color == BLACK) {
            deleteFixup(x, y->parent);
        }
        delete y;
    }

    // 删除修复
    void deleteFixup(Node *x, Node *p) {

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

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

相关文章

VRIK+Unity XR Interaction Toolkit 配置 VR 全身模型(下):实现腿部行走动画

在上一篇教程&#xff1a;VRIKUnity XR Interaction Toolkit 配置 VR 全身模型&#xff08;上&#xff09;&#xff1a;实现上半身的追踪&#xff08;附带VRM模型导入Unity方法和手腕扭曲的解决方法&#xff09;当中&#xff0c;我们通过配置VRIK Unity XR Interaction Toolki…

WiFi(Wireless Fidelity)基础(一)

目录 一、基本介绍&#xff08;Introduction&#xff09; 二、进化发展&#xff08;Evolution&#xff09; 三、PHY帧&#xff08;&#xff08;PHY Frame &#xff09; 四、MAC帧&#xff08;MAC Frame &#xff09; 五、协议&#xff08;Protocol&#xff09; 六、安全&#x…

小松鼠踩一踩游戏

文章目录 一、 介绍和知识点九、UnityFacade 门面设计模式二、 声音全局管理器测试音频代码UI全局管理器父类抽象类 BaseManager子类 UIManager 四、 UI按钮的引用父类 BasePanel子类主面板 MainPanel子类 游戏中 GamePanel子类 游戏结果 ResultPanel 角色动画器、控制角色移动…

性能测试如何做?一套完整的性能测试流程,“我“拒绝背锅...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、测试准备工作 …

中兴B860AV2.1-T(M)-高安版-当贝纯净桌面线刷固件包

中兴B860AV2.1-T(M)-高安版-当贝纯净桌面线刷固件包-内有教程及短接点 特点&#xff1a; 1、适用于对应型号的电视盒子刷机&#xff1b; 2、开放原厂固件屏蔽的市场安装和u盘安装apk&#xff1b; 3、修改dns&#xff0c;三网通用&#xff1b; 4、大量精简内置的没用的软件…

有序序列中插入一个整数

思路&#xff1a; 将输入的要插入的数m依次和数组中的元素进行比较。 思&#xff1a; 在排好序的数组中&#xff0c;从右往左比较还是从左往右比较&#xff1f; 其实都可以&#xff0c;但是我觉得从右边&#xff08;也就是最大的数&#xff09;依次开始比较&#xff0c;程序比较…

纯前端JS实现文件上传解析渲染页面

AI真的能代替前端吗&#xff1f; 回答&#xff1a;不会完全代替 能用吗&#xff1f;复制到项目中只会报错 爆红 ……他完全不能理解你需要什么JavaScript&#xff08;简称JS&#xff09;是一种轻量级的脚本语言&#xff0c;主要用于在Web页面上添加交互行为。它由三个不同的…

5月跳槽有风险,不跳也有?

今天讲讲跳槽。 说实话跳槽是为了寻求更好的发展&#xff0c;但在跳槽前我们也不能确定下家就是更好的归宿&#xff0c;这就更加需要我们审慎地去对待&#xff0c;不能盲目跳槽。 其次&#xff0c;我们离职和跳槽&#xff0c;其中的原因很大一部分是目前薪资不符合预期。 那…

基于python+opencv的人脸识别打卡(手把手教你)

基于pythonopencv的人脸识别打卡 1 创建环境2 准备工作2.1新建members.csv文件2.2新建face文件夹2.3注意事项 3 源码4 操作步骤 1 创建环境 conda create -n face python3.7 conda activate face pip install opencv-python pip install pillow pip install opencv-contrib-py…

基于Redis中zset实现延时任务

目录 概要 一、实现原理 适用场景 二、准备工作 三、代码实现 四、zset的优缺点 优点 缺点 概要 本文章主要记录的是使用Redis中的zset实现延时任务&#xff0c;在工作中&#xff0c;像这样的的延时任务是不可避免的&#xff0c;举个栗子&#xff1a;买一张火车票&#…

企业如何利用网络趋势做好线上营销?

随着互联网的不断发展&#xff0c;线上营销越来越成为企业营销的重要组成部分。如何利用网络趋势做好线上营销&#xff0c;已经成为各大企业关注的焦点。本文将为大家介绍如何利用网络趋势做好线上营销的方法和技巧。 一、了解网络趋势 了解网络趋势是做好线上营销的关键。网络…

uboot移植Linux-SD驱动代码解析

一、uboot与linux驱动 1.1、uboot本身是裸机程序 (1)狭义的驱动概念是指&#xff1a;操作系统中用来具体操控硬件的代码叫驱动 广义的驱动概念是指&#xff1a;凡是操控硬件的代码都叫驱动 (2)裸机程序中是直接使用寄存器的物理地址来操控硬件的&#xff0c;操作系统中必须通…

最新版千帆直播网站系统PHP完整版源码(PC+WAP在线观看视频)附安装教程

最新版千帆直播网站PHP完整版源码&#xff0c;PCWAP在线观看视频直播系统 安装方法&#xff1a; 1、导入数据库文件 zhibo.sql 2、修改数据库配置文件 有多处包含UC配置; 根目录&#xff1a;config.inc.php – config.php 其他路径&#xff1a; Conf/config.php Admin/C…

JVM(三):JVM命令与参数

JVM命令与参数 文章目录 JVM命令与参数JVM参数标准参数-X 参数-XX参数其他参数说明常用参数的意义 常用命令jpsjinfojstatjstackjmap 常用工具jconsolejvisualvm内存分析工具 MATGC日志分析工具内存分析工具 MATGC日志分析工具 经过前面的各种分析学习&#xff0c;我们知道了关…

淦,服务器被人传了后门木马。。。

「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 今天很暴躁&#xff0c;因为睡眠被打扰了。 一个朋友大半夜打我电话&#xff0c;说她云服…

ThingsBoard 接入摄像头方案

0、上图 废话不多说,先给大家来个效果图: 1、概述 最近,我在群里看到有很多兄弟向我咨询摄像头接入到tb的方案,这个就是找对人了,后续我会截图我当初做的东西,其实这个很简单,而且我这种方法是最好的,下面给大家一一道来。 我总结了下面几种情况,其实关键在于摄像头…

QML画布元素

在早些时候的Qt4中加入QML时&#xff0c;一些开发者讨论如何在QtQuick中绘制一个圆形。类似圆形的问题&#xff0c;一些开发者也对于其它的形状的支持进行了讨论。在QtQuick中没有圆形&#xff0c;只有矩形。在Qt4中&#xff0c;如果你需要一个除了矩形外的形状&#xff0c;你需…

浅谈电力物联网在智能配电系统应用

摘要&#xff1a; 在社会经济和科学技术不断发展中&#xff0c;配电网实现了角色转变&#xff0c;传统的单向供电服务形式已经被双向能流服务形式取代&#xff0c;社会多样化的用电需求也得以有效满足。随着物联网技术的发展&#xff0c;泛在电力物联网开始应用于当今的电力系…

使用【SD-WEBUI】插件生成单张图包含多个人物:分区域的提示词

文章目录 &#xff08;零&#xff09;前言&#xff08;一&#xff09;潜变量成对&#xff08;Latent Couple&#xff09;&#xff08;1.1&#xff09;可自组LoRA&#xff08;Composable LoRA&#xff09; &#xff08;二&#xff09;分区扩散&#xff08;Multi Diffusion&#…

@Configuration(proxyBeanMethods = false) 解析

又是美好的一天呀~ 个人博客地址&#xff1a; huanghong.top 往下看看~ Configuration(proxyBeanMethods false) 解析proxyBeanMethods分析总结 Configuration(proxyBeanMethods false) 解析 最近看一些源码的时候&#xff0c;发现很多Configuration配置类上Configuration(p…