C++之红黑树

2023-11-08

红黑树

1. 概念

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

2. 性质

  1. 每个结点不是红色就是黑色
  2. 根节点为黑色
  3. 如果节点为红,其子结点必须为黑
  4. 对于任一结点至NULL(树尾端)的任何路径,所含黑节点数必须相同
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

根据规则4,新增节点必须为红色;根据规则3,新增节点的父节点必须为黑。当新节点根据二叉搜索树的规则达到其插入点,却不符合上述条件,就必须调整颜色并旋转树形。

3. 插入节点

为了延续上图的状态,我们插入一些新节点,看看会发生什么变化,下面有四种不同的典型情况。

假设为图中的RB-tree分别插入3,8, 35, 75,根据二叉搜索树的规则,这四个新节点的落脚点如图所示,他们都破坏了RB-tree的规则,因此我们必须调整树形,也就是旋转树形并改变节点颜色。

  1. 如果U为红,将P和U变为黑色,G变为红色,如果GG节点(曾祖父)为黑,搞定,如果GG为红,就看情况4

  2. 如果U为红,将P和U变为黑色,G变为红色,如果GG仍为红,继续向上调整,直到不再有父子连续为红的情况

  3. 如果U为黑且C为外侧插入,对G进行单旋转,再将G变为红色,P变为黑色

  4. 如果U为黑且C为内侧插入,先对P和G进行单旋转,再将C变为黑色,G变为红色,再对G做一次单旋。

代码实现:

bool Insert(const pair<K, V>& kv) {
    if (_root == nullptr) {
        _root = new Node(kv);
        _root->_col = BLACK;
        return true;
    }

    // 不为空,开始插入,取出当前节点,寻找位置
    Node* cur = _root;
    Node* parent = nullptr;
    while (cur) {
        if (cur->_kv.first > kv.first) { // 要插入的数据比该节点小,往左走
            parent = cur;
            cur = cur->_left;
        }
        else if (cur->_kv.first < kv.first) {// 要插入的数据比该节点大,往右走  
            parent = cur;
            cur = cur->_right;
        }
        else // 相同,返回失败
            return false;
    }
    // 走到这里说明找到了要插入的位置,为要插入的数据创建一个节点
    cur = new Node(kv);
    // 这时需要判断插入位置,与父节点的数据比较大小
    if (parent->_kv.first > kv.first)// 比父节点小,插入左边
        parent->_left = cur;
    else
        parent->_right = cur;
    // 将当前节点的_parent与父节链接
    cur->_parent = parent;

    // 更新颜色 -- 红色节点的左右子树只能为黑,我们默认插入的是红节点
    // cur:当前节点 parent:父节点 grandfather:祖父节点 uncle:叔叔节点
    while (parent && parent->_col == RED) {// 当插入节点的父节点也为红时,就需要调整了  
        // 情况一:cur红 parent红 grandfather黑 uncle存在且为红
        //     g
        //   p   u
        // c 
        Node* grandfather = parent->_parent;
        if (parent == grandfather->_left) {
            Node* uncle = grandfather->_right;
            if (uncle && uncle->_col == RED) {// uncle存在且为红
                grandfather->_col = RED;
                uncle->_col = BLACK;
                parent->_col = BLACK;
                //更新节点,继续向上,查看是否需要更新颜色
                cur = grandfather;
                parent = cur->_parent;
            }
            else {// uncle不存在/uncle为黑  
                //     g
                //   p   u
                // c 
                if (cur == parent->_left) {
                    RotateR(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                }
                else {
                    //     g
                    //   p   u
                    //     c 
                    RotateL(parent);
                    cur->_col = BLACK;
                    grandfather->_col = RED;
                    RotateR(grandfather);
                }
                break;
            }
        }
        else { // (parent == grandfer->_right)
            //     g
            //   u   p
            //		   c 
            Node* uncle = grandfather->_left;
            if (uncle && uncle->_col == RED) { // uncle存在且为红
                grandfather->_col = RED;
                uncle->_col = BLACK;
                parent->_col = BLACK;

                // 更新节点,继续向上,查看是否需要更新颜色
                cur = grandfather;
                parent = cur->_parent;
            }
            else { // uncle不存在/uncle为黑
                //     g
                //   u   p
                //         c 
                if (cur == parent->_right) {
                    RotateL(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                }
                else {
                    //     g
                    //   u   p
                    //     c 
                    RotateR(parent);
                    RotateL(grandfather);
                    cur->_col = BLACK;
                    grandfather->_col = RED;
                }
                break;
            }
        }
    }
    _root->_col = BLACK;
    return true;
}

4. 验证

红黑树的检测分为两步:

  1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
  2. 检测其是否满足红黑树的性质

首先检测根节点的颜色,根节点为红色时,不是RB-tree,然后检测是否有连续的红色节点,一个节点,它的父节点为红色时,不是RB-tree

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);
}

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);
}

5. 红黑树与AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(log2 N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

6. 红黑树的应用

  1. C++ STL库 – map/set、mutil_map/mutil_set
  2. Java 库
  3. Linux内核
  4. 其他一些库
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++之红黑树 的相关文章

随机推荐

  • 最新总结的软件测试宝典,花2天时间阅完,软件测试面试就过了......

    1 测试人员需要何时参加需求分析 如果条件循序 原则上来说 是越早介入需求分析越好 因为测试人员对需求理解越深刻 对测试工作的开展越有利 可以尽早的确定测试思路 减少与开发人员的交互 减少对需求理解上的偏差 2 软件测试与调试的关系 测试条
  • 证书登录失败_爱思助手IPA签名证书申请失败的解决方案

    本文以下解决方法仅供参考 你也可以找朋友给你签一个 实在不行还可以换个工具 毕竟简单的解决问题才是主要目标 比较常见的问题有 证书申请失败 SendAuthenticationRequest em node Your Apple ID or
  • vue 如何实现在函数中触发路由跳转

  • Hql

    1 查询整个映射对象所有字段 直接from查询出来的是一个映射对象 即 查询整个映射对象所有字段 String hql from Users Query query session createQuery hql List
  • Java反编译工具(以反编译android的framework.jar举例)

    framework jar包含android框架层的代码 如果我们在framework层添加了代码 如何确定我们的代码是否真的被编译进入framework jar当中呢 很简单 反编译就好了 下面将介绍2款反编译工具 有一点需要注意 它们都
  • Multi-threaded applications and asynchronous I/O(翻译)

    此文章使用Goolge进行翻译学习使用 原文网址为 http libusb sourceforge net api 1 0 mtasync html 本文章是为了调查 libusb bulk transfer 会阻塞 阻塞时间长达60s的问
  • Spring Boot 整合各种常用技术的代码都在这了

    文章目录 前言 技术清单 简要说明 代码地址 交流 前言 一份慕课网价值 58 元的专栏课程 Spring Boot 趣味私房课 源码 内容为 Spring Boot 与当今主流技术的整合 有很多使用的范例 技术清单 Swagger JUn
  • 老主板BIOS不识别nvem固态硬盘,修改BIOS添加nvme驱动

    以我的技嘉ga f2a68hm s1主板为例 提前到技嘉官网GIGABYTE 技嘉科技下载好对应的BIOS 版本 1 到主板的官方 2 输入主板型号 点击 搜索 3 下载BIOS包 正题开始了 1 准备工具 MMTOOL 2 4GB以上U盘
  • 卡方检验的基本思想是比较实际观察到的频数与期望的频数之间的差异

    卡方检验 Chi Square Test 是一种用于分析分类数据之间的关联性或独立性的统计方法 它通过比较观察到的数据与预期的数据之间的差异来判断两个或多个变量之间是否存在关联 卡方检验通常用于交叉表格 列联表 的分析 例如 研究两种分类变
  • 用java和c++写一个vpn实例的思路

    1 C 开发VPN核心模块 支持更多VPN类型 cpp PPTPContext cpp SSL CTX createPPTPContext PPTPSocket cpp class PPTPSocket public PPTPSocket
  • tolua 判断对象是否为空

    https blog csdn net baidu 39447417 article details 80001371
  • Redis BitMap结构实现签到、连续签到统计

    文章目录 一 利用BitMap结构实现签到功能 1 1 BitMap用法 1 2 代码实现签到功能 1 3 统计连续签到 1 3 1 如何得到本月到今天为止的所有签到数据 1 3 2 如何从后向前遍历每个bit位 1 3 3 代码实现 一
  • 【线代】矩阵的特征值与特征向量原理详解

    如果把矩阵看作是运动 对于运动而言 最重要的当然就是运动的速度和方向 特征值就是运动的速度 特征向量就是运动的方向 参考链接 https www zhihu com question 21874816 answer 181864044 因为
  • 简单的介绍

    最近没有怎么发博客 也不能说自己没有学习 嘿嘿嘿 自己最近在学习框架和java 也很少做ctf 然后把一些学习的产出放在了知识星球或者是github上 想需要一个东西来管理一下 因为自己太懒了 没有搭建个人博客 我还是会发一些我感觉重要的东
  • 算法训练 星际交流

    ALGO 28 星际交流 资源限制 时间限制 1 0s 内存限制 256 0MB 问题描述 人类终于登上了火星的土地并且见到了神秘的火星人 人类和火星人都无法理解对方的语言 但是我们的科学家发明了一种用数字交流的方法 这种交流方法是这样 的
  • numpy的mgrid[]和ogrid[]

    函数说明 mgrid ogrid
  • C语言中常见的一些语法概念和功能

    常用代码 程序入口 int main 函数用于定义程序的入口点 输出 使用 printf 函数可以在控制台打印输出 输入 使用 scanf 函数可以接收用户的输入 条件判断 使用 if else 语句可以根据条件执行不同的代码块 循环结构
  • python使用sqlalchemy判断数据库是否包含某张表

    代码如下 from sqlalchemy import create engine def table exists engine table name 这个函数用来判断表是否存在 with engine connect as con sq
  • NLP基础知识

    1 熟悉 Python 语言 了解一个深度学习框架 Pytorch Tensorflow 或 MXNet 2 熟悉简单的机器学习模型 如 LR SVM HMM 正则化等 3 熟悉简单的深度学习模型 如 word2vec CNN RNN 目录
  • C++之红黑树

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