【简易版tinySTL】 红黑树- 定义, 插入, 构建

news2025/1/21 4:46:06

文章目录

    • 旋转
      • 左旋
      • 右旋
    • 左旋右旋代码实现
    • 红黑树的基本性质
    • 红黑树的插入
    • 红黑树的插入示例
    • 红黑树修复代码实现
    • 参考资料

旋转

image-20240613095136796

对于一个平衡二叉搜索树,左子树高度为4,右子树高度为2,它们的高度差为2,破坏了平衡性(高度差<2才算平衡,因此需要调整二叉树使其平衡)

二叉树最基本的调整方式包括左旋、右旋:

左旋

  • 不平衡点没有左子树

image-20240613095515121

  • 不平衡点有左子树

image-20240613095615583

当结点5左旋时,由于有左子树3,会和结点6冲突,阻碍旋转,我们需要将"冲突的左孩变右孩",即将结点6连在被旋转点5的右孩子上

右旋

  • 不平衡点没有右子树

image-20240613095954955

  • 不平衡点有右子树

image-20240613100021786

当结点14右旋时,由于有右子树17,会和结点9冲突,阻碍旋转,我们需要将"冲突的右孩变左孩",即将结点9连在被旋转点14的左孩子上

左旋右旋代码实现

Node* rightRotate(Node* node)
{
    // node为失衡节点
    Node* l_son = node->left;

    // 不管是否会发生冲突,都把冲突的右孩变左孩
    node->left = l_son->right;
    // 右孩变左孩后,更新父节点(前提它不是空节点)
    if(node->left)
    {
        node->left->parent = node;
    }

    // 更新旋转中心的父节点指向
    l_son->parent = node->parent;
    // 如果当前节点(node)是根节点,则更新根节点为 l_son
    if(l_son->parent == nullptr)
    {
        root = l_son;
    }
    // 如果node是父结点的左子节点
    else if(node->parent->left == node)
    {
        node->parent->left = l_son;
    }
    else
    {
        // 如果node是父结点的右子节点
        node->parent->right = l_son;
    }

    // 把node结点接在l_son右边
    node->parent = l_son;
    l_son->right = node;

    return l_son;
}

Node* leftRotate(Node* node)
{
    Node* r_son = node->right;
    // 提前断掉右结点
    node->right = r_son->left;
    if(r_son->left)
    {
        node->right = r_son->left;
        r_son->left->parent = node;
    }

    r_son->parent = node->parent;
    if(r_son->parent == nullptr)
    {
        root = r_son;
    }
    else if(node->parent->left == node)
    {
        node->parent->left = r_son;
    }
    else
    {
        node->parent->right = r_son;
    }

    r_son->left = node;
    node->parent = r_son;

    return r_son;
}

红黑树的基本性质

空结点也是红黑树的叶子结点,也属于黑结点

  • 根叶黑:根和叶子结点默认为黑色
  • 不红红:不存在连续2个红色结点
  • 黑路同:任一结点到叶子结点的所有路径,黑结点的数量相同(包括空结点/叶子结点)

image-20240613100233691

红黑树的插入

要求:

  1. 默认插入的都是红色
  2. 插入情况讨论:
    • 插入的结点是根结点:直接变黑
    • 插入结点的叔叔结点是红色:叔父爷变色,当前指针指向爷爷结点,修复爷爷结点的红黑树性质
    • 插入结点的叔叔结点是黑色:先旋转(LL、RR、LR、RL),后变色

image-20240620191458222

image-20240620191519731

image-20240620192947162

红黑树的插入示例

假设我们要依次插入:17、18、23、34、27、15、9、6、8、5、25

我们每次插入之后,都要检查是否满足红黑树的性质

微信图片_20240620192221

红黑树修复代码实现

/*
		    O
		   /
		  O 
		 /
		O	<= target
*/
void insertFixup(Node* target) // target是当前插入的结点
{
    // 父结点存在,且出现红红
    while(target->parent && target->parent->color == Color::RED)
    {
        Node* father = target->parent;
        Node* g_father;
        if(father)
            g_father = father->parent;

        // 父是爷的左孩子
        if(g_father && g_father->left == father)
        {
            Node* uncle = g_father->right;
            // 如果叔结点存在,且颜色为红色
            if(uncle && uncle->color == Color::RED)
            {
                // 叔父爷变色
                uncle->color = Color::BLACK;
                father->color = Color::BLACK;
                g_father->color = Color::RED;
                // 将当前指针指向爷结点
                target = g_father;
            }
            // 叔结点不存在或者颜色为黑色(LL/LR)
            else
            {
                // LR
                if(target == g_father->left->right)
                {
                    // 先左旋,旋转函数的输入结点是失衡点
                    leftRotate(father);
                }
                // RR和LR后面的步骤都是一样的
                Node* t = rightRotate(g_father);
                // 变色
                t->color = Color::BLACK;
                t->right->color = Color::RED;
                t->left->color = Color::RED;
                return;
            }
        }

        if(g_father && g_father->right == father)
        {
            Node* uncle = g_father->left;
            if(uncle && uncle->color == Color::RED)
            {
                g_father->color = Color::RED;
                father->color = Color::BLACK;
                uncle->color = Color::BLACK;
                target = g_father;
            }
            else
            {
                // RL
                if(target == g_father->right->left)
                {
                    // !不是旋转父结点
                    rightRotate(father);
                }
                // LL和RL后续都一样
                Node* t = leftRotate(g_father);
                t->color = Color::BLACK;
                t->left->color = Color::RED;
                t->right->color = Color::RED;
                return;
            }
        }
        root->color = Color::BLACK;
    }
}

void insert(Key k, Value v)
{
    Node* node = new Node(k, v);
    Node* p = nullptr;
    Node* cur = root;
    if(this->size == 0)
    {
        root = node;
        size++;
        return;
    }
	// 找到插入点
    while(cur)
    {
        p = cur;
        if(k > cur->key)
        {
            cur = cur->right;
        }
        else if(k < cur->key)
        {
            cur = cur->left;
        }
        else
        {
            std::cout << "the key was in the tree" << std::endl;
            delete node;
            return;
        }
    }
    // 插入新结点
    size++;
    if(k > p->key)
    {
        p->right = node;
    }
    else
    {
        p->left = node;
    }

    node->parent = p;
    if(!p)
    {
        root = node;
    }
    // 修复红黑树
    insertFixup(node);
}

参考资料

红黑树 - 定义, 插入, 构建

红黑树 - 删除

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

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

相关文章

扩展阅读:什么是中断

如果用一句话概括操作系统的原理,那就是:整个操作系统就是一个中断驱动的死循环,用最简单的代码解释如下: while(true){doNothing(); } 其他所有事情都是由操作系统提前注册的中断机制和其对应的中断处理函数完成的。我们点击一下鼠标,敲击一下键盘,执行一个程序,…

忙忙碌碌的混沌之中差点扑了个空而错过年中这条线

文章目录 前言初见端倪混沌初始力不从心心力交瘁拾遗补缺总结 前言 突然意识到过完这个周末已经7月份了&#xff0c;他预示着我的2024年已经过半了&#xff0c;过年回家仿佛还是昨天的事情&#xff0c;怎么转眼间已经到了年中了。心里还是不愿承认这件事&#xff0c;翻开自己2…

使用NFS网关功能将HDFS挂载到本地系统

HDFS安装教程 HDFS安装教程http://t.csdnimg.cn/2ziFd 使用NFS网关功能将HDFS挂载到本地系统 简介 HDFS提供了基于NFS&#xff08;Network File System&#xff09;的插件&#xff0c;可以对外提供NFS网关&#xff0c;供其它系统挂载使用。 NFS 网关支持 NFSv3&#xff0c;并…

etcdctl txn如何使用

TXN 从标准输入中读取多个请求&#xff0c;并将它们应用到单个原子的事务操作中。一个事务包含 一系列的条件、所有条件都满足时要执行的一系列请求、任意条件不满足时要执行的一系列请求。 开启事务&#xff1a; etcdctl txn -i 开启事务后&#xff0c;需要先输入判断条件&a…

Studying-代码随想录训练营day24| 93.复原IP地址、78.子集、90.子集II

第24天&#xff0c;回溯算法part03&#xff0c;牢记回溯三部曲&#xff0c;掌握树形结构结题方法&#x1f4aa; 目录 93.复原IP地址 78.子集 90.子集II 总结 93.复原IP地址 文档讲解&#xff1a;代码随想录复原IP地址 视频讲解&#xff1a;手撕复原IP地址 题目&#xff1…

前端小白必学:对Cookie、localStorage 和 sessionStorage 的简单理解

前言 Cookie、localStorage 和 sessionStorage 作为Web开发领域中广泛采用的三种客户端数据存储技术&#xff0c;它们各自拥有独特的优势、应用场景及限制条件&#xff0c;共同支撑起前端数据管理的多样化需求。也是面试常考题之一&#xff0c;今天就和大家简单谈一下我对它们…

什么是TOGAF架构框架的ADM方法?

ADM是架构开发方法&#xff08; Architecture Development Method&#xff09;&#xff0c;为开发企业架构所要执行的各个步骤以及它们质检的关系进行详细的定义&#xff0c;它是TOGAF规范中最为核心的内容。 ADM的具体步骤&#xff1a; 预备阶段&#xff08;Preliminary Phas…

Redis 高可用(理论)

目录 Redis 高可用 Redis 持久化 RDB 持久化 触发条件 手动触发 自动触发 ##其他自动触发机制## 执行流程 启动时加载 AOF 持久化 执行流程 &#xff08;1&#xff09;命令追加(append) &#xff08;2&#xff09;文件写入(write)和文件同步(sync) &#xff08;3&…

ThreadPoolExecutor 线程回收时机详解

个人博客 ThreadPoolExecutor 线程回收时机详解 | iwts’s blog 总集 想要完整了解下ThreadPoolExecutor&#xff1f;可以参考&#xff1a; 基于源码详解ThreadPoolExecutor实现原理 | iwts’s blog Worker-工作线程管理 线程池设计了内部类Worker&#xff0c;主要是用来…

AI agent是什么,什么技术栈

AI agent&#xff0c;也称为会话代理或聊天机器人&#xff0c; 是一种通过文本或语音模拟人类对话的计算机程序。 它们旨在以自然且引人入胜的方式理解和响应用户输入。 AI agent 被广泛用于各种应用中&#xff0c;包括客户服务、营销、 销售和教育。 有两种主要类型的 AI agen…

2.linux操作系统CPU使用率和平均负载区别

目录 概述cpu使用率区别 结束 概述 linux操作系统CPU 使用率 和 平均负载 区别 负载高并不一定使用率高&#xff0c;有可能 cpu 被占用&#xff0c;但不干活。 cpu使用率 cpu使用率&#xff1a;cpu非空闲态运行的时间占比&#xff0c;反映cpu的繁忙程度&#xff0c;和平均负载…

DS18B20单总线数字温度传感器国产替代MY18E20 MY1820 MY18B20Z MY18B20L(一)

前言 DS18B20是全球第一个单总线数字温度传感器&#xff0c;推出时间已经超过30年&#xff0c;最早由美国达拉斯半导体公司推出&#xff0c;2001年1月&#xff0c;美信以25亿美元收购达拉斯半导体&#xff08;Dallas Semiconductor&#xff09;&#xff0c;而美信在2021年8月被…

使用vscode+git+github管理代码

一、打开代码所在的文件夹 以我最近的看的一个代码项目为例 打开如下 为了方便日后打开&#xff0c;可以把经常看的代码拉出来&#xff0c;然后建一个工作区&#xff0c;后续查看也方方便。直接点开下面第二张图的工作区文件就可以。 二、将代码上传到github 会自动创建一个同名…

成为画图大师,用图表讲故事

这些问题你是否遇到过: 项目总结会上&#xff0c;如果用数据呈现你做的价值&#xff1f; 完善详尽的数据分析得出了让人信服的结论&#xff0c;如何呈现在BOSS面前? 我们要的不是数据&#xff0c;而是数据告诉我们的事实 数据很重要&#xff0c;但只是原料&#xff0c;所以…

分治精炼宝库-----快速排序运用(⌯꒪꒫꒪)੭

目录 一.基本概念: 一.颜色分类&#xff1a; 二.排序数组&#xff1a; 三.数组中的第k个最大元素&#xff1a; 解法一&#xff1a;快速选择算法 解法二&#xff1a;简单粗暴优先级队列 四.库存管理Ⅲ&#xff1a; 解法一&#xff1a;快速选择 解法二&#xff1a;简单粗…

Tcmalloc工具定位内存泄漏问题

内存泄漏问题定位 gperftools工具安装 执行如下操作&#xff1a; git clone https://github.com/gperftools/gperftools.git 注&#xff1a;如果网速较慢&#xff0c;可直接去下载压缩包。 如我下载的地址&#xff1a;https://github.com/gperftools/gperftools/releases/ta…

Unity动画系统(1)

6.1 动画系统基础1-5_哔哩哔哩_bilibili 模型信息 Generic非人型 Configure 虚线圈可以没有&#xff0c;实线圈必须有&#xff0c;15个骨骼是必须的 p313 尾巴、翅膀属于非人型 p314 一般使用create from this model 游戏对象不再旋转 游戏对象不再发生位移 调整中心位置

八月份的护网行动如何参加?

护网行动背景 什么是“护网行动”&#xff1f; 指挥机构∶由公安机关统一组织的"网络安全实战攻防演习"。 护网分为两级演习∶公安部对总部&#xff0c;省厅对省级公司。 什么是“实战攻防演习” 每支队伍3-5 人组成&#xff0c;明确目标系统&#xff0c;不限制攻…

Class Constructors and Destructors (类的构造函数和析构函数)

Class Constructors and Destructors [类的构造函数和析构函数] 1. Declaring and Defining Constructors (声明和定义构造函数)2. Using Constructors (使用构造函数)3. Default Constructors (默认构造函数)4. Destructors (析构函数)5. Improving the Stock Class (改进 Sto…

使用 privacyIDEA 实现 Windows RDP 多因素认证 (MFA)

前言 在等保 2.0 标准中有要求: d&#xff09;应采用口令、密码技术、生物技术等两种或两种以上组合的鉴别技术对用户进行身份鉴别&#xff0c;且其中一种鉴别技术至少应使用密码技术来实现。 可以借助开源的 privacyIDEA 配合 AD 域环境实现 RDP MFA 认证登录以满足上面的要…