【C++模拟实现】map、set容器的模拟实现

news2024/11/26 11:59:58

【C++模拟实现】map、set容器的模拟实现

目录

  • 【C++模拟实现】map、set容器的模拟实现
      • map、set模拟实现的代码(insert部分)
        • 部分一:红黑树的迭代器以及红黑树
        • 部分二:对set进行封装
        • 部分三:对map进行封装
      • 遇到的问题以及解决方案

作者:爱写代码的刚子

时间:2023.9.17

前言:本篇博客有关map、set的模拟实现,其底层采用了红黑树的结构,记录了模拟实现时的问题和解决方案。


map、set模拟实现的代码(insert部分)

部分一:红黑树的迭代器以及红黑树

enum Colour
{
    BLACK,
    RED
};


///红黑树的节点///
template<class T>
struct RBTreeNode
{
    RBTreeNode(const T& kv)
    :_left(nullptr)
    ,_parent(nullptr)
    ,_right(nullptr)
    ,_c(RED)
    ,_kv(kv)
    {}


    RBTreeNode* _left;
    RBTreeNode* _parent;
    RBTreeNode* _right;

    Colour _c; 
    T _kv;

};

//红黑树的迭代器类
template<class T,class Ref,class Ptr>
class __iterator
{
    typedef RBTreeNode<T> Node;
    typedef __iterator<T,Ref,Ptr> Self;
    
    typedef __iterator<T,T&,T*> Iterator;
public:


    __iterator(Node* it)
    :_it(it)
    {}
    //如果这个对象是const迭代器,实现的是普通迭代器转为const迭代器,
    //如果这个对象是普通迭代器,实现的是普通迭代器的拷贝构造。

    __iterator(const Iterator& t)
    :_it(t._it)
    {}

    
    Ref operator*()
    {
        return _it->_kv;
    }

    Ptr operator->()
    {
        return &_it->_kv;
    }

    Self& operator++()//前置++
    {
        if(_it->_right)
        {
            Node* subLeft=_it->_right;
            while(subLeft->_left)
            {
                subLeft=subLeft->_left;
            }

            _it=subLeft;
        }
        else{
            Node* cur = _it;
            Node* parent=_it->_parent;
            while(parent&&parent->_right==cur)
            {
                cur=cur->_parent;
                parent=cur->_parent;
            }
            _it=parent;
        }


        return *this;


    }
    Self operator++(int)//后置++
    {
        __iterator tmp(_it);
        
        if(_it->_right)
        {
            Node* subLeft=_it->_right;
            while(subLeft->_left)
            {
                subLeft=subLeft->_left;
            }

            _it=subLeft;
        }
        else{
            Node* cur = _it;
            Node* parent=_it->_parent;
            while(parent&&parent->_right==cur)
            {
                cur=cur->_parent;
                parent=cur->_parent;
            }
            _it=parent;
        }

        return tmp;
    }
///对--的重载暂时不实现(思路和++相反,将operator++中的_left换成_right,将_right换成_left即可)
  
    bool operator!=(const Self& l) const
    {
        return _it!=l._it;
    }


    bool operator==(const Self& l) const
    {
        return _it==l._it;
    }

    Node* _it;
};


//红黑树的模拟实现
template<class K,class T,class SetKeyOfT>
struct RBTree
{
    
    typedef RBTreeNode<T> Node;
    typedef __iterator<T,T&,T*> iterator;
    typedef __iterator<T,const T&,const T*> const_iterator;
 

    RBTree()
    :_root(nullptr)
    {}
//红黑树迭代器begin和end///
    iterator begin()
    {
        Node* cur=_root;
        if(!_root)
        {
            return nullptr;
        }
        while(cur->_left)
        {
            cur=cur->_left;
        }
        return iterator(cur);
    }
    iterator end()
    {
        return iterator(nullptr);
    }


    const_iterator begin() const
    {
        Node* cur=_root;
        if(!_root)
        {
            return nullptr;
        }
        while(cur->_left)
        {
            cur=cur->_left;
        }
        return const_iterator(cur);
    }

    const_iterator end() const
    {
        return const_iterator(nullptr);
    }


  	Node* Find(const K& key)
		{
			Node* cur = _root;
			SetKeyOfT k;
			while (cur)
			{
				if (k(cur->_data) < key)
				{
					cur = cur->_right;
				}
				else if (k(cur->_data) > key)
				{
					cur = cur->_left;
				}
				else
				{
          return cur;
				}
			}

			return nullptr;
		}

    pair<iterator,bool> insert(const T& kv)
    {
         if(_root==nullptr)
        {
            _root=new Node(kv);
            _root->_c=BLACK;
            return make_pair(iterator(_root),true);
        }
        
            Node* cur=_root;
            Node* parent=nullptr;
            SetKeyOfT k;

            while(cur)
            {
                if(k(cur->_kv)>k(kv))
                {
                    parent=cur;
                    cur=cur->_left;
                }
                else if(k(cur->_kv)<k(kv))
                {
                    parent=cur;
                    cur=cur->_right;
                }
                else{
                    return make_pair(iterator(cur),false);
                }
            }
            cur=new Node(kv);
            Node* newnode = cur;

/
            if(k(parent->_kv)>k(kv))
            {
                parent->_left=cur;
            }
            else{
                parent->_right=cur;
            }
            cur->_parent=parent;

      
        //需要调整的情况
        while(parent&&parent->_c==RED)
        {
            Node* grandfather=parent->_parent;

            if(grandfather->_left==parent)
            {
                Node* uncle=grandfather->_right;
                //判断uncle的几种情况


                if(uncle&&uncle->_c==RED)
                {
                    uncle->_c=parent->_c=BLACK;
                    grandfather->_c=RED;

                    cur=grandfather;
                    parent=cur->_parent;

                
                }
                else 
                {
                    if(cur==parent->_left)
                    {
                        _RotateR(grandfather);

                        grandfather->_c=RED;
                        parent->_c=BLACK;


                    }
                    else{
                        _RotateL(parent);
                        _RotateR(grandfather);

                        grandfather->_c=RED;
                        cur->_c=BLACK;


                    }
                    break;
                }
            }
            else
            {
                Node* uncle=grandfather->_left;
            
                if(uncle&&uncle->_c==RED)
                {
                    uncle->_c=parent->_c=BLACK;
                    grandfather->_c=RED;

                    cur=grandfather;
                    parent=cur->_parent;
                    
                
                }
                else 
                {
                    if(cur==parent->_right)
                    {
                        _RotateL(grandfather);
                        parent->_c=BLACK;
                        grandfather->_c=RED;
                    }
                    else{
                        _RotateR(parent);
                        _RotateL(grandfather);


                        cur->_c=BLACK;
                        grandfather->_c=RED;
                    }
                    break;

                }
                      
            }

        }
        
       
        _root->_c=BLACK;
        return make_pair(iterator(newnode),true);
    
    }
  
    void _RotateR(Node* parent)
    {
        Node*cur=parent->_left;
        Node*curRight=cur->_right;
        Node*ppnode=parent->_parent;
        
        
        cur->_right=parent;
        parent->_left=curRight;

        if(curRight)
        {
            curRight->_parent=parent;
        }
        parent->_parent=cur;
        
        //处理ppnode
        if(parent==_root)
        {
            _root=cur;
            cur->_parent=nullptr;

        }
        else
        {
            if(ppnode->_left==parent)
            {
                ppnode->_left=cur;
            }
            else{
                ppnode->_right=cur;
            }
            cur->_parent=ppnode;
        }
     

    }

    void _RotateL(Node* parent)
    {
        Node* cur=parent->_right;
        Node* curLeft=cur->_left;
        Node* ppnode=parent->_parent;

        cur->_left=parent;
        parent->_right=curLeft;

        if(curLeft)
        {
            curLeft->_parent=cur;
        }
        parent->_parent=cur;

        if(parent==_root)
        {
            _root=cur;
            cur->_parent=nullptr;
        }
        else
        {
            if(ppnode->_left==parent)
            {
                ppnode->_left=cur;
            }
            else{
                ppnode->_right=cur;
            }
            cur->_parent=ppnode;
        }  
    }
    Node* _root;
};

部分二:对set进行封装

template<class K>
class set
{
    struct SetKeyOfT
    {
        const K& operator()(const K& key)//仿函数
        {
            return key; 
        }
    };
public:
    typedef typename RBTree<K,K,SetKeyOfT>::const_iterator iterator;
    typedef typename RBTree<K,K,SetKeyOfT>::const_iterator const_iterator;

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

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

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

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

    pair<iterator,bool> insert(const K& key)
    {
        pair<typename RBTree<K,K,SetKeyOfT>::iterator,bool> ret= _t.insert(key);//这边我们需要注意,右边是普通迭代器,左边是const迭代器,我们需要实现普通迭代器构造const迭代器
        return pair<iterator,bool>(ret.first,ret.second);
    }

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

部分三:对map进行封装

template<class K,class V>
class map
{
    struct MapKeyOfT
    {
        const K& operator()(const pair<K,V>& kv)
        {
            return kv.first;
        }

    };
public:
    typedef typename RBTree<K,pair<const K,V>,MapKeyOfT>::iterator iterator;
    typedef typename RBTree<K,pair<const K,V>,MapKeyOfT>::const_iterator const_iterator;


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


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

    const_iterator begin()  const
    {

        return _t.begin();
    }

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

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


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

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

遇到的问题以及解决方案

  • 【问题】:由于map和set共用一颗树的结构,但是传入的数据类型并不相同,set直接插入key,而map要插入pair。在树里面全部采用统一的模版参数T。在insert的比较大小环节会出现错误,pair不能直接进行大小的比较(虽然库里面pair有进行大小比较的重载函数但是效果并不符合预期)

    【解决】:在set和map的结构体里写一个仿函数,在树里面调用名称相同但是功能不同的仿函数,达到对数据的统一比较的效果。

    map:

    在这里插入图片描述
    在这里插入图片描述

    set:

在这里插入图片描述

在这里插入图片描述

红黑树:

在这里插入图片描述

在这里插入图片描述

红黑树中的SetKeyOfT可以是其他名字,不要和set中的SetKeyOfT搞混了。

  • 【问题】:普通迭代器如何构造const迭代器?

    由于set不能对数据进行修改,所以set的迭代器的下面使用红黑树的const迭代器实现的:

    在这里插入图片描述

    观察set对insert函数的封装:
    在这里插入图片描述

​ 由于set调用红黑树的insert函数而红黑树中insert函数默认返回iterator迭代器,而```pair<iterator,bool> insert(const K& key)``使用的是set中对红黑树中const迭代器封装后的迭代器,insert的返回值和该函数的返回值不相同,需要进行转换,由于红黑树中的迭代器没有实现转换就会出现以下报错:

在这里插入图片描述

【解决】:在红黑树的迭代器中实现普通迭代器对const迭代器的转换:

在这里插入图片描述

  • 【问题】:如何实现map的pair中的key不能被修改?

    【解决】:在这里插入图片描述

    注意!以下传入红黑树的模版参数中也需要加上const:在这里插入图片描述

  • 留意红黑树迭代器中的iterator++中的算法:

在这里插入图片描述

  • 在实现迭代器时,这两个运算符是一定要重载的:
    在这里插入图片描述

  • Map中对[]的重载方法:

    在这里插入图片描述

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

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

相关文章

如何在本地 Linux 主机上实现 Yearning SQL 审核平台的远程访问?

文章目录 前言1. Linux 部署Yearning2. 本地访问Yearning3. Linux 安装cpolar4. 配置Yearning公网访问地址5. 公网远程访问Yearning管理界面6. 固定Yearning公网地址 前言 Yearning 简单, 高效的MYSQL 审计平台 一款MYSQL SQL语句/查询审计工具&#xff0c;为DBA与开发人员使用…

快速了解Apipost

随着数字化转型的加速&#xff0c;API&#xff08;应用程序接口&#xff09;已经成为企业间沟通和数据交换的关键。而在API开发和管理过程中&#xff0c;API文档、调试、Mock和测试的协作显得尤为重要。Apipost正是这样一款一体化协作平台&#xff0c;旨在解决这些问题&#xf…

yolov5使用最新MPDIOU损失函数,有效和准确的边界盒回归的损失,优于GIoU/EIoU/CIoU/EIoU(附代码可用)

文章目录 1. 论文1.1. 主要目的1.2. 设计思路2 代码3.总结1. 论文 MPDIoU: A Loss for Efficient and Accurate Bounding Box Regression (一个有效和准确的边界框损失回归函数) 论文地址 1.1. 主要目的 当预测框与边界框具有相同的纵横比,但宽度和高度值完全不同时,大多数…

【PyTorch 攻略(5/7)】训练和模型

一、说明 训练模型是一个迭代过程。每次迭代称为纪元。该模型对输出进行猜测&#xff0c;计算其猜测中的误差&#xff08;损失&#xff09;&#xff0c;收集误差相对于其参数的导数&#xff0c;并使用梯度下降优化这些参数。 我们从这里加载前面的代码。 %matplotlib inl…

UML活动图

在UML中&#xff0c;活动图本质上就是流程图&#xff0c;它描述系统的活动、判定点和分支等&#xff0c;因此它对开发人员来说是一种重要工具。 活动图 活动是某件事情正在进行的状态&#xff0c;既可以是现实生活中正在进行的某一项工作&#xff0c;也可以是软件系统中某个类…

蓝牙核心规范(V5.4)10.3-BLE 入门笔记之BIS篇

BIS全称:广播同步流 A BIS(Broadcast Isochronous)流提供了在一个发射器(源)和多个接收器(汇)设备之间的广播等时通信。数据以链路层PDUs的形式进行传输,称为BIS数据PDU。控制信息以BIS控制PDUs的形式进行传输。LE-BIS(BIS)逻辑传输在整体数据传输架构中如下图所示。…

github 上传和拉取 support for passward authentication was removed...

参考下面这篇文章 remote: Support for password authentication was removed on August 13, 2021_IT博客技术分享的博客-CSDN博客 但是记得把repositories和 permissions的选项都点上&#xff0c;不然又会报下面的错误。

shiro反序列化漏洞Shiro-550/Shiro-721反序列化

文章目录 shiro反序列化漏洞Shiro-550反序列化漏洞&#xff08;CVE-2016-4437&#xff09;漏洞简介漏洞原理 Shiro-721反序列化漏洞&#xff08;CVE-2019-12422&#xff09;Shiro550和Shiro721的区别是什么漏洞指纹漏洞介绍漏洞原理攻击流程 漏洞复现&#xff1a;CVE-2016-4437…

AOSP Android 系统源码编译出的framework.jar和android.jar之间的区别

简介 AOSP&#xff08;Android Open Source Project&#xff09;编译出的 android.jar 和 framework.jar 都是 Android 平台开发中的重要组件&#xff0c;但它们有不同的作用和用途&#xff1a; android.jar&#xff1a; 用途&#xff1a;android.jar 包含了 Android API 的定…

linux常用命令(4):mkdir命令(创建目录)

文章目录 一、命令简介二、命令格式三、常用示例 一、命令简介 mkdir&#xff08;make directories&#xff09;创建目录。 若指定目录不存在则创建目录。若指定目录已存在&#xff0c;则会提示已存在而不继续创建。 touch与mkdir的区别? 很多人可能会把这个搞混淆&#xff…

Python 函数的说明文档

视频版教程 Python3零基础7天入门实战视频教程 用"“” “”"备注说明&#xff0c;然后我们调用函数的地方&#xff0c;鼠标移动上去就能看到函数的说明。提供代码的可读性。 def add(x, y):"""两数相加函数:param x: 两数相加数x:param y: 两数相加…

Verilog零基础入门(边看边练与测试仿真)-状态机-笔记(7-10讲)

文章目录 第七讲第八讲第九讲第十讲 第七讲 1、最简单的状态机-三角波发生器 1、两种状态的代码&#xff1a; //最简单的状态机&#xff0c;三角波发生器&#xff1b; timescale 1ns/10ps module tri_gen(clk,res,d_out); input clk; input res; o…

vr飞机驾驶舱模拟流程3D仿真演示加大航飞安全法码

众所周知&#xff0c;航空航天飞行是一项耗资大、变量参数很多、非常复杂的系统工程&#xff0c;因此可利用虚拟仿真技术经济、安全及可重复性等特点&#xff0c;进行飞行任务或操作的模拟&#xff0c;以代替某些费时、费力、费钱的真实试验或者真实试验无法开展的场合&#xf…

Lua学习笔记:在Visual Studio中调试Lua源码和打断点

前言 本篇在讲什么 调试Lua源码 本篇需要什么 对Lua语法有简单认知 依赖Visual Studio工具 本篇的特色 具有全流程的图文教学 重实践&#xff0c;轻理论&#xff0c;快速上手 提供全流程的源码内容 ★提高阅读体验★ &#x1f449; ♠ 一级标题 &#x1f448; &…

C#中的方法

引言 在C#编程语言中&#xff0c;方法是一种封装了一系列可执行代码的重要构建块。通过方法&#xff0c;我们可以将代码逻辑进行模块化和复用&#xff0c;提高代码的可读性和可维护性。本文将深入探讨C#中的方法的定义、参数传递、返回值、重载、递归等方面的知识&#xff0c;…

【Hadoop】HDFS API 操作大全

&#x1f341; 博主 "开着拖拉机回家"带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——&#x1f390;开着拖拉机回家_Linux,大数据运维-CSDN博客 &#x1f390;✨&#x1f341; &#x1fa81;&#x1f341; 希望本文能够给您带来一定的帮助&#x1…

Vue3 Ajax(axios)异步

文章目录 Vue3 Ajax(axios)异步1. 基础1.1 安装Ajax1.2 使用方法1.3 浏览器支持情况 2. GET方法2.1 参数传递2.2 实例 3. POST方法4. 执行多个并发请求5. axios API5.1 传递配置创建请求5.2 请求方法的别名5.3 并发5.4 创建实例5.5 实例方法5.6 请求配置项5.7 响应结构5.8 配置…

【配代码演示】Cookie和Session的区别

一、共同之处&#xff1a; cookie和session都是用来跟踪浏览器用户身份的会话方式。 二、工作原理&#xff1a; 1.Cookie的工作原理 &#xff08;1&#xff09;浏览器端第一次发送请求到服务器端 &#xff08;2&#xff09;服务器端创建Cookie&#xff0c;该Cookie中包含用户的…

控制台日志打印console的封装,加入美化与打印开关

控制台日志打印console的封装&#xff0c;加入美化与打印开关 为什么要写这个&#xff1f; 封装这个控制台日志打印工具&#xff0c;主要是在项目中自己做的SDK需要提供给其他开发人员使用&#xff0c;加入了日志美化和打印打开&#xff0c;方便了开发人员查找SDK中的日志&am…

以神龙出行小程序为例,说一些网站技术

注册和登录功能&#xff1a; 用户注册和登录可以使用手机号验证、第三方登录等方式来实现。这需要与后台服务器进行数据交互&#xff0c;并进行身份验证。 数据存储和管理&#xff1a; 用户的个人信息和常用地址需要进行存储和管理。这可以通过数据库来实现&#xff0c;如关系…