深入剖析:基于红黑树实现自定义 map 和 set 容器

news2025/2/25 11:46:40

🌟 快来参与讨论💬,点赞👍、收藏⭐、分享📤,共创活力社区。🌟 


        在 C++ 标准模板库(STL)的大家庭里,mapset可是超级重要的关联容器成员呢😎!它们凭借红黑树这一强大的数据结构,实现了查找、插入和删除等操作的高效运行

        现在,就让我们一步步深入探索如何亲手基于红黑树打造自定义的mapset容器吧🧐!


目录

深入剖析:基于红黑树实现自定义 map 和 set 容器 🚀 

一、红黑树基础结构与操作 🌳

1. 红黑树节点结构 📄

2. 红黑树类基本框架 📐

3. 左旋操作 🔄

4. 右旋操作 🔄

5. 插入修复 🔧

6. 插入操作 📥

7. 查找操作 🔍

8. 红黑树析构函数 🗑️

二、自定义 map 和 set 实现 🛠️

1. 自定义 set 实现 📚

(1)SetKeyOfT 仿函数 📐

2. 自定义 map 实现 🗺️

(1)MapKeyOfT 仿函数 📐

三、测试代码 🧪


一、红黑树基础结构与操作 🌳

1. 红黑树节点结构 📄

红黑树的节点结构包含节点颜色、父节点指针、左右子节点指针以及存储的数据。

  • 实现思路:定义一个结构体来表示红黑树的节点,包含节点颜色、父节点指针、左右子节点指针和存储的数据。为方便后续操作,节点默认颜色设为红色,新插入节点通常为红色,有助于维持红黑树性质。
  • 代码实现
// 定义红黑树节点颜色
enum Colour {
    RED,
    BLACK
};

// 红黑树节点结构体
template<class T>
class RBTreeNode {
public:
    RBTreeNode(const T& data) : _data(data),
        _left(nullptr),
        _right(nullptr),
        _parent(nullptr),
        _col(RED)
    {}

    RBTreeNode<T>* _left;
    RBTreeNode<T>* _right;
    RBTreeNode<T>* _parent;
    Colour _col;

    T _data;
};

2. 红黑树类基本框架 📐

  • 实现思路:定义红黑树类,包含根节点指针。提供插入、查找等基本操作函数声明,用于数据的插入、检索;同时定义左旋、右旋等内部操作函数声明,用于调整树结构,维持红黑树性质。
  • 代码实现
template<class K, class T, class KeyOfT>
class RBTree {
public:
    typedef RBTreeNode<T> Node;
    typedef _TreeIterator<T, T&, T*> iterator;
    typedef _TreeIterator<T, const T&, const T*> const_iterator;

    iterator begin();
    iterator end();
    const_iterator begin() const;
    const_iterator end() const;
    pair<Node*, bool> insert(const T& data);

private:
    Node* _root = nullptr;
    void RotateL(Node* parent);
    void RotateR(Node* parent);
    void RotateRL(Node* parent);
    void RotateLR(Node* parent);
};

3. 左旋操作 🔄

 

实现步骤👇

  • 保存关键节点指针

    • 保存 parent 的右子节点 subR 和 subR 的左子节点 subRL
  • 调整 parent 的右子节点

    • 将 parent 的右子节点更新为 subRL
    • 如果 subRL 不为空,将 subRL 的父节点指针指向 parent
  • 调整 subR 的左子节点

    • 将 subR 的左子节点更新为 parent
    • 保存 parent 的父节点 parentParent
    • 将 parent 的父节点指针指向 subR
  • 更新根节点或父节点的子节点指针

    • 如果 parent 是根节点(即 parentParent 为空),将 subR 设为新的根节点,并将 subR 的父节点指针置为空。
    • 否则,根据 parent 是 parentParent 的左子节点还是右子节点,更新 parentParent 的相应子节点指针为 subR,并将 subR 的父节点指针指向 parentParent

代码实现: 

template<class K, class T, class KeyOfT>
void RBTree<K, T, KeyOfT>::RotateL(Node* parent) { 
    Node* subR = parent->_right;
    Node* subRL = subR->_left;

    parent->_right = subRL;
    if (subRL) {
        subRL->_parent = parent;
    }

    subR->_left = parent;
    Node* parentParent = parent->_parent;
    parent->_parent = subR;

    if (parentParent == nullptr) {
        _root = subR;
        subR->_parent = nullptr;
    }
    else {
        if (parentParent->_left == parent) {
            parentParent->_left = subR;
        }
        else {
            parentParent->_right = subR;
        }
        subR->_parent = parentParent;
    }
}

4. 右旋操作 🔄

  • 实现思路:右旋操作以 parent 为中心,交换其与左子节点 subL 位置,调整 subLR 指针,更新根或父节点指向维持平衡
  • 代码实现
template<class K, class T, class KeyOfT>
void RBTree<K, T, KeyOfT>::RotateR(Node* parent) { 
    Node* subL = parent->_left;
    Node* subLR = subL->_right;

    parent->_left = subL->_right;
    if (subLR) {
        subLR->_parent = parent;
    }

    subL->_right = parent;
    Node* parentParent = parent->_parent;
    parent->_parent = subL;

    if (parentParent == nullptr) {
        _root = subL;
        subL->_parent = nullptr;
    }
    else {
        if (parentParent->_left == parent) {
            parentParent->_left = subL;
        }
        else {
            parentParent->_right = subL;
        }
        subL->_parent = parentParent;
    }
}

5. 插入修复 🔧

  • 实现思路:插入新节点后,红黑树的性质可能会被破坏,需要进行修复操作。修复操作主要根据新节点的父节点和叔节点的颜色进行分类处理,通过旋转和颜色调整来恢复红黑树的性质。
  • 代码实现:
pair<Node*,bool> insert(const T& data)
{
    if (_root == nullptr)
    {
        _root = new Node(data);
        _root->_col = BLACK;
        return make_pair(_root, true);
    }
    Node* cur = _root;
    Node* parent = nullptr;
    KeyOfT kot;

    while (cur)
    {
        parent = cur;
        if (kot(cur->_data) < kot(data))
        {
            cur = cur->_right;
        }
        else if (kot(cur->_data) > kot(data))
        {
            cur = cur->_left;
        }
        else
        {
            return make_pair(cur, false);
        }
    }

    //新增结点给红色
    cur = new Node(data);
    Node* newNode = cur;
    cur->_parent = parent;
    if (kot(parent->_data) < kot(cur->_data))
    {
        parent->_right = cur;
    }
    else
    {
        parent->_left = cur;
    }

    while (parent && parent->_col == RED)
    {
        //找叔叔
        Node* grandfather = parent->_parent;
        Node* uncle = nullptr;
        if (parent == grandfather->_left)
        {
            uncle = grandfather->_right;
        }
        else
        {
            uncle = grandfather->_left;
        }
        if (uncle && uncle->_col == RED)//满足规则一,父亲和叔叔变黑,爷爷变红
        {
            parent->_col = uncle->_col = BLACK;
            grandfather->_col = RED;

            //继续向上更新
            cur = grandfather;
            parent = grandfather->_parent;
        }
        else //叔叔 不存在 或  存在且为黑
        {
            if (parent == grandfather->_left && cur == parent->_left)//右单旋
            {
                RotateR(grandfather);
                //变色
                parent->_col = BLACK;
                grandfather->_col = RED;
            }
            else if (parent == grandfather->_right && cur == parent->_right)//左单旋
            {
                RotateL(grandfather);
                //变色
                parent->_col = BLACK;
                grandfather->_col = RED;
            }
            else if (parent == grandfather->_left && cur == parent->_right)//折射,双旋
            {
                RotateLR(grandfather);
                //变色
                cur->_col = BLACK;
                grandfather->_col = RED;
            }
            else if (parent == grandfather->_right && cur == parent->_left)//折射,双旋
            {
                RotateRL(grandfather);
                //变色
                cur->_col = BLACK;
                grandfather->_col = RED;
            }
        }
    }

    _root->_col = BLACK;

    return make_pair(newNode, true);
}

 

6. 插入操作 📥

  • 实现思路:首先找到合适的插入位置,创建新节点并插入到树中,然后调用插入修复函数来恢复红黑树的性质。
  • 代码实现
template<class K, class T, class KeyOfT>
pair<typename RBTree<K, T, KeyOfT>::Node*, bool> RBTree<K, T, KeyOfT>::insert(const T& data) {
    if (_root == nullptr) {
        _root = new Node(data);
        _root->_col = BLACK;
        return make_pair(_root, true);
    }
    Node* cur = _root;
    Node* parent = nullptr;
    KeyOfT kot;

    while (cur) {
        parent = cur;
        if (kot(cur->_data) < kot(data)) {
            cur = cur->_right;
        }
        else if (kot(cur->_data) > kot(data)) {
            cur = cur->_left;
        }
        else {
            return make_pair(cur, false);
        }
    }

    // 插入新节点并设置颜色
    cur = new Node(data);
    Node* newNode = cur;
    cur->_parent = parent;
    if (kot(parent->_data) < kot(cur->_data)) {
        parent->_right = cur;
    }
    else {
        parent->_left = cur;
    }

    while (parent && parent->_col == RED) {
        // 情况分类
        Node* grandfather = parent->_parent;
        Node* uncle = nullptr;
        if (parent == grandfather->_left) {
            uncle = grandfather->_right;
        }
        else {
            uncle = grandfather->_left;
        }
        if (uncle && uncle->_col == RED) { // 叔叔节点存在且为红色
            parent->_col = uncle->_col = BLACK;
            grandfather->_col = RED;

            // 继续向上调整
            cur = grandfather;
            parent = grandfather->_parent;
        }
        else { // 叔叔节点不存在或为黑色
            if (parent == grandfather->_left && cur == parent->_left) { // 左左情况
                RotateR(grandfather);
                // 调整颜色
                parent->_col = BLACK;
                grandfather->_col = RED;
            }
            else if (parent == grandfather->_right && cur == parent->_right) { // 右右情况
                RotateL(grandfather);
                // 调整颜色
                parent->_col = BLACK;
                grandfather->_col = RED;
            }
            else if (parent == grandfather->_left && cur == parent->_right) { // 左右情况
                RotateLR(grandfather);
                // 调整颜色
                cur->_col = BLACK;
                grandfather->_col = RED;
            }
            else if (parent == grandfather->_right && cur == parent->_left) { // 右左情况
                RotateRL(grandfather);
                // 调整颜色
                cur->_col = BLACK;
                grandfather->_col = RED;
            }
        }
    }

    _root->_col = BLACK;

    return make_pair(newNode, true);
}

7. 查找操作 🔍

  • 实现思路:从根节点开始,根据键的大小比较,递归地在左子树或右子树中查找目标节点。
  • 代码实现
template<class K, class T, class KeyOfT>
RBTreeNode<T>* RBTree<K, T, KeyOfT>::Find(const K& key) {
    RBTreeNode<T>* current = _root;
    KeyOfT kot;
    while (current) {
        if (key < kot(current->_data)) {
            current = current->_left;
        }
        else if (key > kot(current->_data)) {
            current = current->_right;
        }
        else {
            return current;
        }
    }
    return nullptr;
}

8. 红黑树析构函数 🗑️

  • 实现思路:采用后序遍历的方式递归删除树中的所有节点,释放内存。
  • 代码实现
template<class K, class T, class KeyOfT>
RBTree<K, T, KeyOfT>::~RBTree() {
    auto destroyTree = [](RBTreeNode<T>* node) {
        if (node != nullptr) {
            destroyTree(node->left);
            destroyTree(node->right);
            delete node;
        }
    };
    destroyTree(_root);
}

二、自定义 map 和 set 实现 🛠️

1. 自定义 set 实现 📚

(1)SetKeyOfT 仿函数 📐

  • 实现思路:由于set中存储的元素就是键,因此仿函数直接返回元素本身。
  • 代码实现
namespace zdf {
    template<class K>
    class set {
    public:
        struct SetKeyOfT {
            const K& operator()(const K& key) {
                return key;
            }
        };
        // 定义迭代器类型
        typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
        typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;

        iterator begin() const {
            return _t.begin();
        }

        iterator end() const {
            return _t.end();
        }

        pair<iterator, bool> insert(const K& key) {
            return _t.insert(key);
        }

    private:
        RBTree<K, K, SetKeyOfT> _t;
    };
}

2. 自定义 map 实现 🗺️

(1)MapKeyOfT 仿函数 📐

  • 实现思路map中存储的是键值对,仿函数从键值对中提取键。
  • 代码实现
namespace zdf {
    template<class K, class V>
    class map {
        struct MapKeyOfT {
            const K& operator()(const std::pair<K, V>& kv) {
                return kv.first;
            }
        };
    public:
        typedef typename RBTree<K, std::pair<const K, V>, MapKeyOfT>::iterator iterator;
        typedef typename RBTree<K, std::pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;


        iterator begin() {
            return _t.begin();
        }

        iterator end() {
            return _t.end();
        }

        V& operator[](const K& key) {
            std::pair<iterator, bool> ret = insert(std::make_pair(key, V()));
            return ret.first->second;
        }

        pair<iterator, bool> insert(const std::pair<K, V>& kv) {
            return _t.insert(kv);
        }

    private:
        RBTree<K, std::pair<const K, V>, MapKeyOfT> _t;
    };
}

三、测试代码 🧪

#include <iostream>

int main() {
    // 测试set
    zdf::set<int> s;
    s.insert(3);
    s.insert(1);
    s.insert(2);

    // 测试map
    zdf::map<int, std::string> m;
    m.insert({ 1, "one" });
    m.insert({ 2, "two" });
    m[3] = "three";

    std::cout << "Map value at key 3: " << m[3] << std::endl;

    return 0;
}

        通过以上步骤,我们基于红黑树实现了自定义的mapset容器,每个函数的实现思路和代码都进行了详细讲解。这些实现展示了红黑树在关联容器中的重要应用,以及如何通过封装和扩展红黑树来实现高效的数据存储和操作。 🎉

欢迎关注我 👉【A charmer】

 

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

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

相关文章

20-R 绘图 - 饼图

R 绘图 - 饼图 R 语言提供来大量的库来实现绘图功能。 饼图&#xff0c;或称饼状图&#xff0c;是一个划分为几个扇形的圆形统计图表&#xff0c;用于描述量、频率或百分比之间的相对关系。 R 语言使用 pie() 函数来实现饼图&#xff0c;语法格式如下&#xff1a; pie(x, l…

第438场周赛:判断操作后字符串中的数字是否相等、提取至多 K 个元素的最大总和、判断操作后字符串中的数字是否相等 Ⅱ、正方形上的点之间的最大距离

Q1、判断操作后字符串中的数字是否相等 1、题目描述 给你一个由数字组成的字符串 s 。重复执行以下操作&#xff0c;直到字符串恰好包含 两个 数字&#xff1a; 从第一个数字开始&#xff0c;对于 s 中的每一对连续数字&#xff0c;计算这两个数字的和 模 10。用计算得到的新…

软考教材重点内容 信息安全工程师 第17章 网络安全应急响应技术原理与应用

17.1 网络安全应急响应概述 网络安全应急响应是针对潜在发生的网络安全事件而采取的网络安全措施。 17.1.1 网络安全应急响应概念 网络安全应急响应是指为应对网络安全事件&#xff0c;相关人员或组织机构对网络安全事件进行监测、预警、分析、响应和恢复等工作。 17.2.3 网络安…

点击修改按钮图片显示有问题

问题可能出在表单数据的初始化上。在 ave-form.vue 中&#xff0c;我们需要处理一下从后端返回的图片数据&#xff0c;因为它们可能是 JSON 字符串格式。 vue:src/views/tools/fake-strategy/components/ave-form.vue// ... existing code ...Watch(value)watchValue(v: any) …

Node.js技术原理分析系列——Node.js的perf_hooks模块作用和用法

Node.js 是一个开源的、跨平台的 JavaScript 运行时环境&#xff0c;它允许开发者在服务器端运行 JavaScript 代码。Node.js 是基于 Chrome V8 引擎构建的&#xff0c;专为高性能、高并发的网络应用而设计&#xff0c;广泛应用于构建服务器端应用程序、网络应用、命令行工具等。…

用DeepSeek来帮助学习three.js加载3D太极模形

画一个平面的太极图是很容易&#xff0c;要实现3D的应该会很难 一、参考3D模形效果 看某网页看到一个效果&#xff0c;像一个3D太极球&#xff0c;觉得挺有趣&#xff0c;挺解压的&#xff0c;想进一步去了解下这是如何实现 效果&#xff1a; 链接地址&#xff1a; http://www.…

【JavaEE进阶】Spring Boot配置文件

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗 如有错误&#xff0c;欢迎指出~ 目录 SpringBoot配置⽂件 举例: 通过配置文件修改端口号 配置⽂件的格式 properties基本语法 读取配置⽂件 properties配置文件的缺点 yml配置⽂件 yml基本语法 yml和proper…

学习通用多层次市场非理性因素以提升股票收益预测

“Learning Universal Multi-level Market Irrationality Factors to Improve Stock Return Forecasting” 论文地址&#xff1a;https://arxiv.org/pdf/2502.04737 Github地址&#xff1a;https://github.com/lIcIIl/UMI 摘要 深度学习技术与量化交易相结合&#xff0c;在股…

【Godot4.3】基于绘图函数的矢量蒙版效果与UV换算

概述 在设计圆角容器时突发奇想&#xff1a; 将圆角矩形的每个顶点坐标除以对应圆角矩形所在Rect2的size&#xff0c;就得到了顶点对应的UV坐标。然后使用draw_colored_polygon&#xff0c;便可以做到用图片填充圆角矩形的效果。而且这种计算的效果就是图片随着其填充的图像缩…

DeepSeek开源周Day1:FlashMLA引爆AI推理性能革命!

项目地址&#xff1a;GitHub - deepseek-ai/FlashMLA 开源日历&#xff1a;2025-02-24起 每日9AM(北京时间)更新&#xff0c;持续五天&#xff01; ​ 一、开源周震撼启幕 继上周预告后&#xff0c;DeepSeek于北京时间今晨9点准时开源「FlashMLA」&#xff0c;打响开源周五连…

通过恒定带宽服务器调度改进时间敏感网络(TSN)流量整形

论文标题 英文标题&#xff1a;Improving TSN Traffic Shaping with Constant Bandwidth Server Scheduling 中文标题&#xff1a;通过恒定带宽服务器调度改进时间敏感网络&#xff08;TSN&#xff09;流量整形 作者信息 作者&#xff1a;Benjamin van Seggelen 指导教师&am…

如何查看图片的原始格式

问题描述&#xff1a;请求接口的时候&#xff0c;图片base64接口报错&#xff0c;使用图片url请求正常 排查发现是图片格式的问题&#xff1a; 扩展名可能被篡改&#xff1a;如果文件损坏或扩展名被手动修改&#xff0c;实际格式可能与显示的不同&#xff0c;需用专业工具验证…

赛前启航 | 三场重磅直播集结,予力微软 AI 开发者挑战赛!

随着微软 AI 开发者挑战赛的火热进行&#xff0c;赛前指导直播已成为众多参赛者获取技术干货、灵感碰撞和实战技巧的绝佳平台。继前两期的精彩呈现&#xff0c;第三、四、五期直播即将接连登场&#xff0c;为开发者们带来更加深入的 AI 技术剖析和项目实战指引。无论你是想进一…

VMware安装Centos 9虚拟机+设置共享文件夹+远程登录

一、安装背景 工作需要安装一台CentOS-Stream-9的机器环境&#xff0c;所以一开始的安装准备工作有&#xff1a; vmware版本&#xff1a;VMware Workstation 16 镜像版本&#xff1a;CentOS-Stream-9-latest-x86_64-dvd1.iso &#xff08;kernel-5.14.0&#xff09; …

【HarmonyOS Next】地图使用详解(一)

背景 这系列文章主要讲解鸿蒙地图的使用&#xff0c;当前可以免费使用&#xff0c;并提供了丰富的SDK给开发者去自定义控件开发。目前可以实现个性化显示地图、位置搜索和路径规划等功能&#xff0c;轻松完成地图构建工作。需要注意的是&#xff0c;现在测试只能使用实体手机去…

顶刊配图复现:Origin+DeepSeek完美协同

学习目标&#xff1a; &#xff08;1&#xff09;软件掌握熟练安装并配置Origin&#xff0c;掌握基础操作与核心功能。学会利用Origin进行多类型图表绘制及美化。掌握DeepSeek的数据清洗、统计分析与可视化方法。&#xff08;2&#xff09;设计能力理解顶刊图表的设计原则&…

Scratch032(百发百中)

提示:知识回顾 1、排列克隆体的方法 2、复习“发送广播并等待”积木 3、“获取第几个字符”积木的使用 4、使用角色显示得分 前言 提示:中国射箭拥有悠久的历史,是最早进入教育体系的运动项目之一,君子六艺中“礼,乐,射,御,书,数”的射 ,就是指的射箭。这节课我带你…

Minio分布式多节点多驱动器集群部署

Minio分布式多节点多驱动器集群部署 Minio分布式多节点多驱动器集群部署节点规划先决条件开放防火墙端口设置主机名更新域名映射文件时间同步存储要求内存要求 增加虚拟机磁盘(所有机器都要执行)部署分布式 MinIO测试上传与预览测试高可用MinIO 配置限制模拟单节点磁盘故障模拟…

【信号量】

信号量 目录操作系统信号信号的默认处理动作示例解释信号的捕获与处理使用 signal 函数使用 sigaction 函数 信号的阻塞 信号的生命周期1. 信号产生2. 信号在进程中注册3. 信号在进程中注销4. 信号处理main 7 signal命令含义使用场景手册页包含的关键信息1. 信号概述2. 信号列表…