C++---map/set封装
分析注map/set底层都是红黑树实现的。可能有的人会想set是key的map是key/value的那底层难道这两个容器分别写一个红黑树吗不是的经过前人对源码的剖析大佬将红黑树搞成了一个类模板上层是set和map各自两个类模板通过传不同的模板参数给底层的红黑树模板达到一个复用的效果感觉跟stack/queue的容器适配器实现思路很像。迭代器实现思路迭代器主要是要解决两个重要的部分就是operator和operator--。先说吧。首先要明确set/map的迭代器在便利的时候走的是中序也就意味着begin()是整棵数的最左结点end()就为空因为end()表示最后一个有效位置的下一个位置。一次就走到中序便利的下一个结点处中序便利是左根右把当前结点看成根的话之后都到的就是其右子树的最左结点。如下图10结点之后到15结点30结点之后到35结点。那如果右子树为空呢看下图加入现在便利到15结点其右子树为空这就表示以10结点为根的这棵子树已经全部便利完了因为中序的顺序是左根右嘛接下来要去看下图中蓝色框出的那棵子树的根结点在其父结点也就是18结点的左边还是右边如果是左边的话之后就来到18结点如果在右边假设现在便利到50结点其右子树为空且右子树在以40结点为根的整棵子树的右边该子树便利完以40结点为根的子树在以30为根的子树的右边说明以30为根的子树便利完了看下图中紫色框出部分的子树的根结点30就是在18结点的右边就说明整棵树已经走完了。说的有点复杂其实意思就是严格遵循左根右的便利原则去考虑问题。右子树便利完了就说明整棵子树便利完了看看整棵子树在根结点的左还是右在左就继续从根结点开始便利在右就说明整棵树就便利完了。--的思路就跟完全相反了右根左具体就不细讲了在课件上有代码贴出来了。key不能修改的问题最简单的一种方式就是加上const从底层规避掉能否修改的问题。operator[]之前博客里说过这个是怎么实现的本质就是复用insertinsert的返回值是个pair插入成功就返回true插入失败就返回false但iterator自始至终都是指向着key所在的结点。总结整体的书写逻辑根据本文最开始列出的那几个一个个板块来。总体而言上层的set/map就是一个躯壳底层最内核的东西就是红黑树。set.h#pragma once #includeRBTree.h namespace xxc { templateclass K class set { //仿函数 struct SetKeyOfT { const K operator()(const K key) { return key; } }; public: typedef typename RBTreeK, const K, SetKeyOfT::Iterator iterator; iterator begin() { return _t.Begin(); } iterator end() { return _t.End(); } pairiterator, bool insert(const K k) { return _t.Insert(k); } private: RBTreeK, const K, SetKeyOfT _t; }; }map.h#pragma once #includeRBTree.h namespace xxc { templateclass K, class V class map { struct MapKeyOfT { const K operator()(const pairK, V kv) { return kv.first;//返回key } }; public: typedef typename RBTreeK, pairconst K, V, MapKeyOfT::Iterator iterator; iterator begin() { return _t.Begin(); } iterator end() { return _t.End(); } pairiterator, bool insert(const pairK, V kv) { return _t.Insert(kv); } V operator[](const K key) { pairiterator, bool ret insert({ key, V() }); return ret.first-second; } private: RBTreeK, pairconst K, V, MapKeyOfT _t; }; }RBTree.h#pragma once #includeiostream using namespace std; enum Color { RED, BLACK }; //上层传下来是key就是key是pair就是pair templateclass T struct RBTreeNode { T _data; RBTreeNodeT* _left; RBTreeNodeT* _right; RBTreeNodeT* _parent; Color _col; RBTreeNode(const T data) :_data(data) , _left(nullptr) , _right(nullptr) , _parent(nullptr) { } }; templateclass T, class Ref, class Ptr struct TreeIterator { typedef RBTreeNodeT Node; typedef TreeIteratorT, Ref, Ptr Self; Node* _node; TreeIterator(Node* node) :_node(node) { } Ref operator*() { return _node-_data; } Ptr operator-() { return (_node-_data); } bool operator!(const Self s) const { return _node ! s._node; } bool operator(const Self s) const { return _node s._node; } 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; //如果父结点存在并且当前子树在根节点的右边则说明当前子树已经便利完 //这里真得带着课件里的图一起看有点说不清楚 //把博客里贴着的那张图50结点当成cur40结点当成parent带着代码一点点走 //你可以理解为这里是从最小的那棵子树开始判断是不是根的右子树一直到整棵树 while (parent cur parent-_right) { cur parent; parent parent-_parent; } //如果整棵树都走完了则parent为nullptr_node赋值为nullptr //如果某一棵子树走完了相当于是左根右的左走完了继续走根 //此时parent就为新子树的根。 _node parent; } return *this; } }; //T表示的就是set里的key或者map里的key/value也就是pair //由于set和map共用的RBTreeKeyOfT获取的就是set里的key或者map里的key方便下边Insert里的比较逻辑 templateclass K, class T, class KeyOfT class RBTree { typedef RBTreeNodeT Node; public: typedef TreeIteratorT, T, T* Iterator; typedef TreeIteratorT, const T, const T* ConstIterator; Iterator Begin() { Node* min _root; //有可能为空树此时Begin就为nullptr while (min min-_left) { min min-_left; } return Iterator(min); } Iterator End() { return Iterator(nullptr); } //data有可能是key有可能是pair看上层调用的时候传过来的是什么 pairIterator, bool Insert(const T data) { if (_root nullptr) { _root new Node(data); _root-_col BLACK; return { Iterator(_root), true }; } KeyOfT kot; Node* parent nullptr; Node* cur _root; while (cur) { if (kot(data) kot(cur-_data)) { parent cur; cur cur-_right; } else if (kot(data) kot(cur-_data)) { parent cur; cur cur-_left; } else { return { Iterator(cur), false }; } } cur new Node(data); Node* newnode cur;//这里要保存一下因为下边调整颜色的时候cur会往上走会变化 cur-_col RED; if (kot(data) kot(parent-_data)) { parent-_right cur; } else { parent-_left cur; } cur-_parent parent; while (parent parent-_col RED) { Node* g parent-_parent; if (g-_left parent) { Node* u g-_right; if (u u-_col RED) { parent-_col BLACK; u-_col BLACK; g-_col RED; cur g; parent cur-_parent; } else { if (cur parent-_left) { RotateR(g); parent-_col BLACK; g-_col RED; } else { RotateL(parent); RotateR(g); cur-_col BLACK; g-_col RED; } break; } } else { Node* u g-_left; if (u u-_col RED) { parent-_col BLACK; u-_col BLACK; g-_col RED; cur g; parent cur-_parent; } else { if (cur parent-_right) { RotateL(g); parent-_col BLACK; g-_col RED; } else { RotateR(parent); RotateL(g); cur-_col BLACK; g-_col RED; } break; } } } _root-_col BLACK; return { Iterator(newnode), true }; } //Find只要传keyRBTree第一个模板参数在这里派上了用处 Node* Find(const K key) { KeyOfT kot; Node* cur _root; while (cur) { if (kot(cur-data) key) { cur cur-_right; } else if (kot(cur-data) key) { cur cur-_left; } else { return cur; } } return nullptr; } private: 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; subL-_parent nullptr; } else { if (parentParent-_left parent) { parentParent-_left subL; } else { parentParent-_right subL; } subL-_parent parentParent; } } 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; } } private: Node* _root nullptr; };测试代码#define _CRT_SECURE_NO_WARNINGS 1 #includeset.h #includemap.h #includeiostream #includevector #includestring using namespace std; void test_set() { xxc::setint s; s.insert(4); s.insert(1); s.insert(2); s.insert(12); s.insert(22); s.insert(2223); s.insert(-2); s.insert(0); xxc::setint::iterator it s.begin(); while (it ! s.end()) { // *it 1; cout *it ; it; } cout endl; } void test_map() { xxc::mapstring, string dict; dict.insert({ sort, 排序 }); dict.insert({ left, 左边 }); dict.insert({ right, 右边 }); dict[left] 左边剩余; // 修改 dict[insert] 插入; // 插入修改 dict[string]; // 插入 xxc::mapstring, string::iterator it dict.begin(); while (it ! dict.end()) { // 不能修改first可以修改second //it-first x; it-second x; cout it-first : it-second endl; it; } cout endl; } int main() { test_set(); test_map(); return 0; }

相关新闻

凤凰网资讯爬虫实战:从零构建国际新闻分类数据采集系统

凤凰网资讯爬虫实战:从零构建国际新闻分类数据采集系统

摘要 在信息过载的时代,高效获取结构化新闻数据成为数据分析和舆情监控的基础。本文以凤凰网资讯频道为研究对象,系统讲解如何构建一个完整的国际新闻分类爬虫系统。文章从需求分析、技术选型、反爬策略、数据解析、存储优化到定时调度,提供全链路解决方案。不同于简单Demo…

2026/10/13 20:58:25 阅读更多 →
商标局公告爬虫实战:Python采集最新注册商标信息(2026版)

商标局公告爬虫实战:Python采集最新注册商标信息(2026版)

一、引言:商标数据为何值得爬取 商标作为企业无形资产的核心组成部分,其注册公告蕴含着极高的商业情报价值。通过定时抓取商标局发布的注册商标公告,我们可以实现: 竞品监测:实时追踪竞争对手的商标布局,预判其业务扩张方向 品牌保护:发现与自己品牌近似的恶意抢注,及…

2026/10/13 4:20:45 阅读更多 →
MTKClient终极指南:轻松解锁和刷机联发科设备

MTKClient终极指南:轻松解锁和刷机联发科设备

MTKClient终极指南:轻松解锁和刷机联发科设备 【免费下载链接】mtkclient MTK reverse engineering and flash tool 项目地址: https://gitcode.com/gh_mirrors/mt/mtkclient MTKClient是一款功能强大的开源工具,专门用于联发科芯片设备的底层操作…

2026/10/11 11:54:56 阅读更多 →

最新新闻

Windows平台Rust开发环境配置与优化指南

Windows平台Rust开发环境配置与优化指南

1. Rust语言与Windows环境适配背景Rust作为一门系统级编程语言,其编译器工具链对操作系统底层有着深度依赖。在Windows平台进行Rust开发需要特别注意环境配置的差异性——与Linux/macOS不同,Windows存在MSVC和GNU两种ABI(应用程序二进制接口&…

2026/10/14 2:25:24 阅读更多 →
长波红外超构透镜偏振复用FDTD仿真全流程解析

长波红外超构透镜偏振复用FDTD仿真全流程解析

1. 项目背景与核心价值长波红外超构透镜偏振复用聚焦技术是近年来光学领域的前沿研究方向。作为一名光学工程师,我在实际项目中发现传统红外光学系统存在体积大、重量沉、功能单一等问题。而基于超构表面的偏振复用透镜,能在单层平面上实现多通道光学功能…

2026/10/14 2:25:24 阅读更多 →
2026 查重率和 AIGC 率都飘红?一站式降AI率软件实测推荐

2026 查重率和 AIGC 率都飘红?一站式降AI率软件实测推荐

一、前言:2026 高校论文审核新难题随着高校学术审核体系不断升级,知网、维普等主流检测平台全面上线AIGC 智能检测功能,当代毕业生的论文写作与修改迎来双重考验。以往论文仅需攻克重复率超标问题,如今还要规避AI写作痕迹检测风险…

2026/10/14 2:25:24 阅读更多 →
摄影工作室AI提效手册:婚纱写真儿童摄影的修图、营销与交付(v1.0)

摄影工作室AI提效手册:婚纱写真儿童摄影的修图、营销与交付(v1.0)

摄影工作室AI提效手册:婚纱、写真、儿童摄影的修图营销与交付版本 v1.0  2026年10月写在前面:这本手册解决什么这本手册写给三类人:开婚纱影楼的老板与修图主管、拍个人写真和家庭照的独立摄影师、做儿童摄影工作室的店长与后期。你大概率已…

2026/10/14 2:25:24 阅读更多 →
流水线设计实战:从冒险机制到乱序执行的性能优化

流水线设计实战:从冒险机制到乱序执行的性能优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 2:25:24 阅读更多 →
【数据可视化-155】第十五届全运会乒乓球男单半决赛:樊振东VS王楚钦技术分析报告

【数据可视化-155】第十五届全运会乒乓球男单半决赛:樊振东VS王楚钦技术分析报告

🧑 博主简介:曾任某智慧城市类企业算法总监,目前在美国市场的物流公司从事高级算法工程师一职,深耕人工智能领域,精通python数据挖掘、可视化、机器学习等,发表过AI相关的专利并多次在AI类比赛中获奖。CSDN人工智能领域的优质创作者,提供AI相关的技术咨询、项目开发和个…

2026/10/14 2:24:24 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →