【C++】STL —— map和set的模拟实现

news/2024/4/27 3:50:11/文章来源:https://blog.csdn.net/sjsjnsjnn/article/details/128096084

目录

一、基础铺垫

二、基本结构分析

1. 节点结构分析 

2. 模板参数中仿函数分析

三、正向迭代器

四、封装完成的红黑树

五、map的模拟实现

六、set的模拟实现


一、基础铺垫

        在前面的博客中我们了解了map和set的基本使用,以及对二叉搜索树、AVL树和红黑树的概念和简单实现有了一定的了解后,对于map和set来说,他们的底层实现都是基于红黑树的。我们知道set是key的模型、map是key/value的模型,那么一棵红黑树是如何实现出map和set的呢?

二、基本结构分析

1. 节点结构分析 

        以上是截取的部分源码结构,我们可以看到set的模板参数是Key,map的模板参数是KeyT;set将Key这个模板参数typedef成了key_type和value_type,map将Key这个模板参数typedef成了key_type、将T这个模板参数typedef成了data_type;

        他们同时调用红黑树时,都是用的value_type,对应set的value_type是Key,对应map的value_type是pair<const key,T>;这意味着红黑树只看传过来的value_type是什么类型的,当上层容器是set的时候,结点当中存储的是键值Key;当上层容器是map的时候,结点当中存储的就是<Key, Value>键值对。

结点结构: 

enum Colour
{RED,BLACK
};template<class T>
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;//存储的数据类型:如果是set,T对应的就是K;如果是map,T对应的就是pairColour _col;RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr), _col(RED), _data(data){}
};

红黑树基本结构: 

template<class k, class T>
class RBTree
{typedef RBTreeNode<T> Node;
private:Node* _root;
};

map的基本结构: 

namespace mlg
{template<class k, class v>class map{private:RBTree<k, pair<k, v>> _t;};
}

set的基本结构: 

namespace mlg
{template<class k>class set{private:RBTree<k, k> _t;};
}

2. 模板参数中仿函数分析

        因为红黑树中存储的是T类型,有可能是Key,也有可能是<Key, Value>键值对;那么当我们需要进行结点的键值比较时,应该如何获取结点的键值呢?

        当上层容器是set的时候T就是键值Key,直接用T进行比较即可,但当上层容器是map的时候就不行了,此时我们需要从<Key, Value>键值对当中取出键值Key后,再用Key值进行比较。

        因此,上层容器map需要向底层红黑树提供一个仿函数,用于获取T当中的键值Key,这样一来,当底层红黑树当中需要比较两个结点的键值时,就可以通过这个仿函数来获取T当中的键值了。

map的仿函数:

namespace mlg
{template<class k, class v>class map{public:struct MapKeyOfT{const k& operator()(const pair<k, v>& kv){return kv.first;}};private:RBTree<k, pair<k, v>, MapKeyOfT> _t;};
}

set的仿函数:

namespace mlg
{template<class k>class set{public:struct SetKeyOfT{const k& operator()(const k& k){return k;}};private:RBTree<k, k, SetKeyOfT> _t;};
}

三、正向迭代器

        红黑树的正向迭代器实际上就是对结点指针进行了封装,因此在正向迭代器当中实际上就只有一个成员变量,那就是正向迭代器所封装结点的指针。 

template<class T, class Ref, class Ptr>
struct RBTreeIterator
{typedef RBTreeNode<T> Node;//结点的类型typedef RBTreeIterator<T, Ref, Ptr> Self;//正向迭代器的类型Node* _node;//正向迭代器所封装结点的指针RBTreeIterator(Node* node)//根据所给结点指针构造一个正向迭代器:_node(node){}Ref operator*(){return _node->_data;//返回结点数据的引用}Ptr operator->(){return &_node->_data;//返回结点数据的指针}//前置++/*如果当前结点的右子树不为空,则++操作后应该找到其右子树当中的最左结点。如果当前结点的右子树为空,则++操作后应该在该结点的祖先结点中,找到孩子不在父亲右的祖先。*/Self& operator++(){if (_node->_right)//结点的右子树不为空{Node* min = _node->_right;while (min->_left){min = min->_left;}_node = min;}else  //结点的右子树为空{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = cur->_parent;parent = parent->_parent;}_node = parent;}return *this;}//前置--/*如果当前结点的左子树不为空,则--操作后应该找到其左子树当中的最右结点。如果当前结点的左子树为空,则--操作后应该在该结点的祖先结点中,找到孩子不在父亲左的祖先。*/Self& operator--(){if (_node->_left){Node* max = _node->_left;while (max->_right){max = max->_right;}_node = max;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}//判断两个正向迭代器是否不同bool operator!=(const Self& s)const{return _node != s._node;}//判断两个正向迭代器是否相同bool operator==(const Self& s)const{return _node == s._node;}
};

四、封装完成的红黑树

#pragma once
#include <iostream>
#include <algorithm>
using namespace std;enum Colour
{RED,BLACK
};template<class T>
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Colour _col;RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr), _col(RED), _data(data){}
};template<class T, class Ref, class Ptr>
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T, Ref, Ptr> Self;Node* _node;RBTreeIterator(Node* node):_node(node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}//前置++Self& operator++(){if (_node->_right){Node* min = _node->_right;while (min->_left){min = min->_left;}_node = min;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = cur->_parent;parent = parent->_parent;}_node = parent;}return *this;}//前置--Self& operator--(){if (_node->_left){Node* max = _node->_left;while (max->_right){max = max->_right;}_node = max;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}bool operator!=(const Self& s)const{return _node != s._node;}bool operator==(const Self& s)const{return _node == s._node;}
};//set RBTree<k, k>
//map RBTree<k, pair<k, v>>
template<class k, class T, class keyofT>
class RBTree
{typedef RBTreeNode<T> Node;
public:typedef RBTreeIterator<T, T&, T*> iterator;typedef RBTreeIterator<T, const T&, const T*> const_iterator;iterator begin(){Node* min = _root;while (min && min->_left){min = min->_left;}return iterator(min);}iterator end(){return iterator(nullptr);}RBTree():_root(nullptr){}RBTree(const RBTree<k, T, keyofT>& t){_root = Copy(t._root);}Node* Copy(Node* root){if (root == nullptr){return nullptr;}Node* newRoot = new Node(root->_data);newRoot->_col = root->_col;newRoot->_left = Copy(root->_left);newRoot->_right = Copy(root->_right);if (newRoot->_left){newRoot->_left->_parent = newRoot;}if (newRoot->_right){newRoot->_right->_parent = newRoot;}return newRoot;}RBTree<k, T, keyofT>& operator=(RBTree<k, T, keyofT> t){swap(_root, t._root);return *this;}~RBTree(){Destroy(_root);_root = nullptr;}void Destroy(Node* root){if (root == nullptr){return;}Destroy(root->_left);Destroy(root->_right);delete root;}iterator Find(const k& key){Node* cur = _root;keyofT kot;while (cur){if (kot(cur->_data) < key){cur = cur->_right;}else if (kot(cur->_data) > key){cur = cur->_left;}else{return iterator(cur);}}return end();}pair<iterator, bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(iterator(_root), true);}keyofT kot;Node* parent = nullptr;Node* cur = _root;while (cur){if (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else{return make_pair(iterator(cur), false);}}cur = new Node(data);Node* newnode = cur;cur->_col = RED; // 新增节点if (kot(parent->_data) < kot(data)){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}// 控制平衡while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;// 1、uncle存在且为红if (uncle && uncle->_col == RED){// 变色+继续向上处理parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else // 2 + 3、uncle不存在/ 存在且为黑{if (cur == parent->_left){// 单旋RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// 双旋RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else // parent == grandfather->_right{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){// 变色+继续向上处理parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else // 2 + 3、uncle不存在/ 存在且为黑{if (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(iterator(newnode), true);}//左单选void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL){subRL->_parent = parent;}Node* parentParent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (parent == _root)//这里表明原来是根{_root = subR;subR->_parent = nullptr;}else{if (parentParent->_left == parent){parentParent->_left = subR;}else{parentParent->_right = subR;}subR->_parent = parentParent;}}//由单旋void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR){subLR->_parent = parent;}Node* parentParent = parent->_parent;subL->_right = parent;parent->_parent = subL;if (parent == _root)//这里表明原来是根{_root = subL;_root->_parent = nullptr;}else{if (parentParent->_left == parent){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}}void InOrder(){_InOrder(_root);}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}
private:Node* _root;
};

五、map的模拟实现

#include "RBTree.h"
namespace mlg
{template<class k, class v>class map{public:struct MapKeyOfT{const k& operator()(const pair<k, v>& kv){return kv.first;}};typedef typename RBTree<k, pair<k, v>, MapKeyOfT>::iterator iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}pair<iterator, bool> insert(const pair<k, v>& kv){return _t.Insert(kv);}iterator find(const k& key){return _t.Find(key);}v& operator[](const k& key){auto ret = _t.Insert(make_pair(key, v()));return ret.first->second;}private:RBTree<k, pair<k, v>, MapKeyOfT> _t;};
}

六、set的模拟实现

#include "RBTree.h"
namespace mlg
{template<class k>class set{public:struct SetKeyOfT{const k& operator()(const k& k){return k;}};typedef typename RBTree<k, k, SetKeyOfT>::iterator iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}pair<iterator, bool> insert(const k& key){return _t.Insert(key);}iterator find(const k& key){return _t.Find(key);}private:RBTree<k, k, SetKeyOfT> _t;};
}

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IPv6进阶:IPv6 过渡技术之 NAT64(IPv6 节点主动访问 IPv4 节点-地址池方式)

实验拓扑 PC1是IPv4网络的一个节点&#xff0c;处于Trust安全域&#xff1b;PC2是IPv6网络的一个节点&#xff0c;处于Untrust安全域。 实验需求 完成防火墙IPv4、IPv6接口的配置&#xff0c;并将接口添加到相应的安全域&#xff1b;在防火墙上配置NAT64的IPv6前缀3001::/64&…

【毕业设计】30-基于单片机矿井瓦斯_气体浓度_烟雾浓度报警设计(原理图+源代码+仿真+答辩论文+答辩PPT)

【毕业设计】30-基于单片机矿井瓦斯/气体浓度/烟雾浓度报警设计&#xff08;原理图源代码仿真答辩论文答辩PPT&#xff09; 文章目录【毕业设计】30-基于单片机矿井瓦斯/气体浓度/烟雾浓度报警设计&#xff08;原理图源代码仿真答辩论文答辩PPT&#xff09;任务书设计说明书摘要…

网络套接字编程(UDP协议)

文章目录预备知识socket&#xff08;网络套接字&#xff09;编程接口简单的UDP网络程序增加多用户可以互相通信预备知识 网络字节序 大端存储&#xff1a;数据的高字节内容保存在内存的低地址处&#xff0c;数据的低字节内容保存在内存的高地址处 小端存储&#xff1a;数据的高…

global关键字、python实现ATM简单功能

目录 一.局部变量、全局变量 二.global关键字 演示 三.编写ATM程序 要求 详细步骤 存在问题 改进 完整代码 一.局部变量、全局变量 1.什么是局部变量 作用范围在函数内部&#xff0c;在函数外部无法使用 2.什么是全局变量 在函数内部和外部均可使用 3.如何将函数内定…

[附源码]SSM计算机毕业设计校园自行车租售管理系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

高等数学(第七版)同济大学 习题10-3 (前9题)个人解答

高等数学&#xff08;第七版&#xff09;同济大学 习题10-3&#xff08;前9题&#xff09; 函数作图软件&#xff1a;Mathematica 1.化三重积分I∭Ωf(x,y,z)dxdydz为三次积分&#xff0c;其中积分区域Ω分别是\begin{aligned}&1. \ 化三重积分I\iiint_{\Omega}f(x, \ y, …

【C++】类型转换

目录 一、C语言风格类型转换 二、C风格类型转换 1.static_case 2.reinterpret_case 3、const_case 4、dynamic_case 三、RTTI 总结 一、C语言风格类型转换 在C语言中&#xff0c;如果赋值运算符左右两侧类型不同&#xff0c;或者形参与实参类型不匹配&#xff0c;或者返…

【正点原子FPGA连载】 第二十章 LCD触摸屏实验摘自【正点原子】DFZU2EG/4EV MPSoC 之FPGA开发指南V1.0

1&#xff09;实验平台&#xff1a;正点原子MPSoC开发板 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id692450874670 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html 第二十章 LCD触摸…

Vue.js 加入高德地图的实现方法

一、功能需求 1.根据输入内容进行模糊查询&#xff0c;选择地址后在地图上插上标记&#xff0c;并更新经纬度坐标显示 2.在地图点击后&#xff0c;根据回传的左边更新地址信息和坐标显示 二、准备 1.申请高德地图账号&#xff0c;创建应用 2.在应用管理中 获得key 和安全密…

[附源码]Python计算机毕业设计Django常见Web漏洞对应POC应用系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

Python学习:json对象与string相互转换教程

首先要明确&#xff0c;python里有json这个库&#xff0c;但并没有json这个类&#xff0c;所以所谓的json对象本质上就是一个dict&#xff1b;而json这个库&#xff0c;用于实现dict到string、string到dict的互转。 更具体一点&#xff0c;json对象&#xff08;dict&#xff0…

Linux网络编程——IO多路复用

文章目录1&#xff0c;I/O模型2&#xff0c;阻塞I/O 模式2.1&#xff0c;读阻塞&#xff08;以read函数为例&#xff09;2.2&#xff0c;写阻塞3&#xff0c;非阻塞I/O模式3.1&#xff0c;非阻塞I/O模式的实现&#xff08;fcntl()函数、ioctl() 函数&#xff09;3.1.1&#xff…

leetcode 343. 整数拆分(动态规划)

题目链接&#xff1a;343. 整数拆分 动态规划 (1) 确定 dpdpdp 数组下标含义&#xff1a; dp[i]dp[i]dp[i]: 将 iii 拆分为至少两个正整数之后的最大乘积&#xff1b; (2) 确定递推公式&#xff1a; 当 i≥2i \ge 2i≥2 时, 设 jjj 是 iii 拆分出来的第一个正整数&#xff0c…

关于uni-app小程序接入微信登录

https://uniapp.dcloud.net.cn/api/plugins/login.html#login 官网上有关于uni.login()的说明&#xff0c;如果是要微信登录&#xff0c;则需要wx.login()。 小程序登录 | 微信开放文档 如下图&#xff0c;在小程序管理平台生成AppSecret&#xff0c;同时将AppId在HubilderX中…

swift @State @Published @ObservedObject sink

State struct ContentView: View {State private var isRain truevar body: some View {VStack {Image(systemName: isRain ? "cloud.rain.fill" : "sun.max.fill").resizable().frame(width: 100, height: 100)Text(isRain ? "我們淋著大雨不知何…

【PS-7】移动工具

目录 移动工具快捷键【v】&#xff08;英文状态&#xff09; 多文件间拖拽图层对象 快捷键【ALT】复制图层 【ALTSHIFT】只能垂直/水平/45角地去复制图层 4个方向键可以微调图层的位置 变换控件 对齐分布 【题外话】设置参考线颜色 【题外话】快捷键【F12】让已经动过…

实验三-----数据库

一、实验目的 1.掌握SQL Server Management Studio中SQL 查询操作&#xff1b; 2.掌握SQL 的单表查询命令&#xff1b; 3.掌握SQL 的连接查询操作&#xff1b; 4.掌握SQL 的嵌套查询操作&#xff1b; 5.掌握SQL 的集合查询操作。 二、实验环境 1&#xff0e;实验室名称&…

[附源码]计算机毕业设计springboot海南琼旅旅游网

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

前端单元测试,更进一步

前端测试2022 如果从 2014 年 Jest 的第一个版本发布开始计算&#xff0c;前端开发领域工程化的单元测试能力已经发展了八年有余。Jest 集成了 Jasmine 等以往各种被证明有效的单元测试框架和断言等工具&#xff0c;也可以用来完成包含外部接口服务的集成测试等。最近几年热门的…

xxl-job安装部署

一、简介 XXL-JOB是一个分布式任务调度平台&#xff0c;其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线&#xff0c;开箱即用。 中文文档English Documentation 二、安装 xxl-job需要的提前安装好以下环境&#xff1a;jdk、m…