C++ map 全面解析:从基础用法到实战技巧

C++ map 全面解析:从基础用法到实战技巧

🔥个人主页:Cx330🌸

❄️个人专栏:《C语言》《LeetCode刷题集》《数据结构-初阶》《C++知识分享》

《优选算法指南-必刷经典100题》《Linux操作系统》:从入门到入魔

🌟心向往之行必能至


🎥Cx330🌸的简介:


目录

前言:

一、map 核心概念与特性

1. 什么是 map?

2. 头文件与命名空间

3. map模板参数与内部类型

4. 常见初始化方式:

二、map 基础用法(必备知识点)

2.1 构造与初始化

2.2 遍历

1. 迭代器遍历(三种方式):

2. 范围for遍历

3. 结构化绑定(C++17支持):

2.3 插入操作(insert):

2.4 查找与删除(find/erase)

2.5 核心特性:operator [] 的多功能性

三. map 与 multimap 的差异

最佳实践:

四、实战技巧:map经典例题

4.1 随机链表的复制

4.2 前k个高频单词

结尾:


前言:

在 C++ 标准库的容器中,std::map 是一款兼具有序性高效查找的核心容器,广泛应用于字典映射、配置存储、数据去重等场景。它基于红黑树(平衡二叉搜索树)实现,既能保证键(key)的唯一性,又能自动对键进行排序,是开发中不可或缺的工具。本文将从基础概念到进阶实战,带你全面掌握 std::map 的用法

一、map 核心概念与特性

1. 什么是 map?

std::map 是一种关联容器,存储的是键值对(key-value pair) 集合。其核心特点是:

  • 键(key)唯一:不允许重复的键,插入重复键会失败(或覆盖,取决于用法);
  • 自动排序:默认按键的升序排列(可自定义排序规则);
  • 底层实现:基于红黑树,插入、查找、删除的时间复杂度均为 O(log n)(n 为元素个数);

std::map 参考文档map - C++ Reference

2. 头文件与命名空间

使用 map 需包含头文件 <map>,并使用 std 命名空间(或显式指定 std::map):

#include <map> using namespace std;

3. map模板参数与内部类型

// map 模板定义 template <class Key, // 键的类型(Key) class T, // 值的类型(Value,typedef 为 mapped_type) class Compare = less<Key>, // 键的比较方式(默认升序) class Alloc = allocator<pair<const Key, T>> // 空间配置器 > class map; // 核心内部类型 typedef pair<const Key, T> value_type; // 红黑树节点存储的键值对(Key 不可改) typedef Key key_type; // 键的类型 typedef T mapped_type; // 值的类型 

value_typemap 存储的是 pair<const Key, T> 类型,其中 Key 被 const 修饰,意味着不能通过迭代器修改 Key(会破坏红黑树结构),但可以修改 T(Value)

 Compare:默认用 less<Key> 实现升序,若需降序,可传入 greater<Key>(如 map<int, int, greater<int>>

pair文档pair - C++ Reference

4. 常见初始化方式:

// 1. 空 map(默认键升序) map<int, string> m1; // 2. 初始化列表(C++11 及以上) map<int, string> m2 = { {1, "apple"}, {2, "banana"}, {3, "cherry"} }; // 3. 拷贝构造 map<int, string> m3(m2); // 4. 范围构造(从迭代器区间拷贝) map<int, string> m4(m2.begin(), m2.end()); // 5. 自定义排序规则(降序) map<int, string, greater<int>> m5 = {{1, "a"}, {2, "b"}}; // 结果:2→b, 1→a

二、map 基础用法(必备知识点)

2.1 构造与初始化

map 支持多种构造方式,包括默认构造、迭代器区间构造、初始化列表构造等,实际开发中初始化列表构造最常用:

#include<iostream> #include<map> #include<string> using namespace std; void test_map01() { // 1. 默认构造(空 map) map<string, string> dict1; // 2. 初始化列表构造(C++11,比较推荐) map<string, string> dict2 = { {"sort", "排序"}, {"left", "左边"}, {"right", "右边"} }; // 3. 迭代器区间构造(从其他 map 或容器拷贝) map<string, string> dict3(dict2.begin(), dict2.end()); // 4. 拷贝构造 map<string, string> dict4(dict3); cout << "dict2 初始化结果:" << endl; map<string, string>::iterator it = dict2.begin(); while (it != dict2.end()) { cout << it->first << ":" << it->second << endl; ++it; } cout << endl; } int main() { test_map01(); }

 

 

注意:  dict 遍历结果按 Key 升序排列(left < right < sort,按 ASCII 码比较),体现 map 自动排序特性

2.2 遍历

map 的迭代器是双向迭代器,支持 ++/-- 操作,遍历方式包括 “迭代器循环”“范围 for”“结构化绑定”,其中结构化绑定(C++17+)最简洁:

1. 迭代器遍历(三种方式):

如上图,我们采取的就是迭代器遍历的方式,再给大家展示一遍:

 

 

核心细节map 的 iterator 和 const_iterator 都不能修改 Key,但 iterator 可以修改 Value;若只需读取,优先用 const auto& 传引用,避免拷贝开销

注意:这三种方式任选其一即可

2. 范围for遍历

 

 

3. 结构化绑定(C++17支持):

 

 

2.3 插入操作(insert):

map 的 insert 接口用于插入键值对,返回 pair<iterator, bool>,其中:

  • iterator:指向插入成功的新节点,或已存在的相同 Key 节点;
  • bool:true 表示插入成功,false 表示 Key 已存在、插入失败
#include<iostream> #include<map> #include<string> using namespace std; void test_map1() { map<string, string> dict; //C++98 pair<string, string> kvl("sort","排序"); dict.insert(kvl); dict.insert(pair<string, string>("left","左边")); dict.insert(make_pair("right", "右边")); //C++11 dict.insert({ "right","右边" }); dict.insert({ kvl,pair<string,string>("right","右边") }); dict.insert({ {"string","字符串"},{"map","地图,映射"}}); //key相同就不会再插入,value不相同也不会插入 dict.insert({ "left","左边xxx" }); map<string, string>::iterator it = dict.begin(); while (it != dict.end()) { //cout << (*it).first << ":" << (*it).second << " "; cout << it->first << ":" << it->second << endl; //cout << it.operator->()->first << ":" << it.operator->()->second << " "; ++it; } cout << endl; }

2.4 查找与删除(find/erase)

(1)查找:find 与 count

  • find(Key):查找指定 Key,返回指向该 Key 的迭代器;若不存在,返回 end()(O(log N) 效率);
  • count(Key):返回 Key 的出现次数(map 中 Key 唯一,故返回 0 或 1,可间接用于查找)

2)删除:erase
erase 
支持三种删除形式:删除指定迭代器、删除指定 Key、删除迭代器区间,其中 “删除迭代器” 需先通过 find 确认 Key 存在,避免删除 end() 崩溃:

void test_map03() { map<string, string> dict = { {"sort", "排序"}, {"left", "左边"}, {"right", "右边"} }; // 1. 查找 Key 'left' auto pos = dict.find("left"); if (pos != dict.end()) { cout << "找到 Key 'left',值:" << pos->second << endl; // 2. 删除迭代器指向的节点(安全删除) dict.erase(pos); cout << "删除 Key 'left' 后:" << endl; for (const auto& [k, v] : dict) { cout << k << ":" << v << endl; } } // 3. 直接删除指定 Key(返回删除的个数,map 中 0 或 1) size_t del_cnt = dict.erase("right"); cout << "删除 Key 'right',影响个数:" << del_cnt << endl; // 4. 删除迭代器区间(删除所有元素) dict.erase(dict.begin(), dict.end()); cout << "删除所有元素后,map 大小:" << dict.size() << endl; } int main() { //test_map01(); //test_map1(); test_map03(); //test_map2(); //test_map3(); return 0; }

2.5 核心特性:operator [] 的多功能性

map 的 operator[] 是最灵活的接口,兼具 “插入、查找、修改” 三种功能,其内部实现依赖 insert,逻辑如下:

void test_map5() { map<string, int> countMap; // 统计水果出现次数 string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "香蕉" }; // 这样很麻烦 //for (auto& e : arr) //{ // auto it = countMap.find(e); // if (it != countMap.end()) // { // it->second++; // } // else // { // countMap.insert({ e,1 }); // } //} // 场景1:插入 + 修改(统计次数,最常用) for (const auto& fruit : arr) { // 若 fruit 不存在:插入 {fruit, 0},返回 0 的引用,++ 后变为 1; // 若 fruit 已存在:返回现有 Value 的引用,++ 后次数增加; countMap[fruit]++; } cout << "水果统计结果:" << endl; for (const auto& [fruit, cnt] : countMap) { cout << fruit << ":" << cnt << endl; } cout << endl; // 场景2:纯粹插入(Key 不存在时,插入默认 Value) map<string, string> dict; dict["insert"]; // 插入 { "insert", "" }(string 默认空) cout << "插入 'insert' 后,值:" << dict["insert"] << endl; // 场景3:插入 + 修改(Key 不存在时插入,存在时修改) dict["left"] = "左边"; // 插入 { "left", "左边" } dict["left"] = "左边(修改后)"; // 修改 Value 为 "左边(修改后)" cout << "修改 'left' 后,值:" << dict["left"] << endl; // 场景4:纯粹查找(Key 存在时,返回 Value 引用) cout << "查找 'left',值:" << dict["left"] << endl; // 对比:at() 接口(仅支持查找+修改,Key 不存在时抛异常,不插入) dict.at("left") = "左边(at 修改)"; // 合法,修改 Value // dict.at("nonexist") = "不存在"; // 抛出异常:out_of_range } int main() { test_map5(); }

关键区别operator[] 在 Key 不存在时会插入默认值,而 at() 会抛异常;若需 “严格查找”(不存在时不插入),优先用 find;若需 “统计次数”“快速赋值”,优先用 operator[]


三. map 与 multimap 的差异

multimap 和 map 的使用基本完全类似,主要区别点在于 multimap 支持关键值 key 冗余,那么 insert/find/count/erase 都围绕着支持关键值 key 冗余有所差异,这里跟 set 和 multiset 完全一样,比如 find 时,有多个 key,返回中序第一个。其次就是 multimap 不支持 [],因为支持 key 冗余,[] 就只能支持插入了,不能支持修改

最佳实践:

void test_multimap() { //multimap没有[] multimap<string, string> dict; dict.insert({ "right", "右边" }); dict.insert({ "left", "左边" }); dict.insert({ "right", "右边xx" }); dict.insert({ "right", "右边" }); for (const auto& [k, v] : dict) { cout << k << ":" << v << endl; } cout << endl; } int main() { test_multimap(); } 

四、实战技巧:map经典例题

4.1 随机链表的复制

题目链接:

138. 随机链表的复制 - 力扣(LeetCode)

数据结构初阶阶段,为了控制随机指针,我们将拷贝结点链接在原节点的后面解决,后面拷贝节点还得解下来链接,非常麻烦,我们直接让 {原节点,拷贝节点}映射回到map容器,控制随机指
针会非常简单方便,这里体现了map在解决⼀些问题时的价值,完全是降维打击

代码实现:

class Solution { public: Node* copyRandomList(Node* head) { map<Node*,Node*> NodeMap; Node*copyhead=nullptr,*copytail=nullptr; Node*cur=head; while(cur) { if(copyhead==nullptr) { copyhead=copytail=new Node(cur->val); } else{ copytail->next=new Node(cur->val); copytail=copytail->next; } //原节点和拷贝节点通过map,kv存储 NodeMap.insert({cur,copytail}); cur=cur->next; } //处理random cur=head; Node* copy = copyhead; while(cur) { if(cur->random==nullptr) copy->random = nullptr; else // 查找 copy->random=NodeMap[cur->random]; cur=cur->next; copy=copy->next; } return copyhead; } }; 

4.2 前k个高频单词

题目链接:

692. 前K个高频单词 - 力扣(LeetCode)

用排序找前k个单词,因为map中已经对key单词排序过,也就意味着遍历map时,次数相同的单词,字典序小的在前面,字典序大的在后面,那么我们将数据放到vector中用一个稳定的排序就可以实现上面特殊要求,但是 sort底层是快排 ,是不稳定的,所以我们要用 stable_sort,他是稳定的

class Solution { public: struct kv_pair { bool operator()(const pair<string, int>& x, const pair<string, int>& y) { return x.second > y.second; } }; vector<string> topKFrequent(vector<string>& words, int k) { map<string,int> CountMap; for(auto& e:words) { CountMap[e]++; } vector<pair<string,int>> v(CountMap.begin(),CountMap.end()); //sort(v.begin(),v.end(),kv_pair()); // 得稳定排序 stable_sort(v.begin(),v.end(),kv_pair()); vector<string> ret; for(size_t i=0;i<k;i++) { ret.push_back(v[i].first); } return ret; } }; 

思路2:(vector 与 priority_queue)
map统计出的次数的数据放到vector中排序,或者放到priority_queue中来选出前k个。利用仿函数强行控制次数相等的,字典序小的在前面

解法一:vector

class Solution { public: struct kv_pair { // 次数多的在前面,次数相等的时候,字典序小的在前面 bool operator()(const pair<string, int>& x, const pair<string, int>& y) { return x.second > y.second || (x.second == y.second && x.first < y.first); } }; vector<string> topKFrequent(vector<string>& words, int k) { map<string,int> CountMap; for(auto& e:words) { CountMap[e]++; } vector<pair<string,int>> v(CountMap.begin(),CountMap.end()); sort(v.begin(),v.end(),kv_pair()); vector<string> ret; for(size_t i=0;i<k;i++) { ret.push_back(v[i].first); } return ret; } }; 

解法二:priority_queue:

class Solution { public: struct kv_pair { // 次数多的在前面,次数相等的时候,字典序小的在前面 bool operator()(const pair<string, int>& x, const pair<string, int>& y) { // 要注意优先级队列底层是反的,大堆要实现小于比较,所以这里次数相等,想要字典序小的在前面要比较字典序大的为真 return x.second < y.second || (x.second == y.second && x.first > y.first); } }; vector<string> topKFrequent(vector<string>& words, int k) { map<string,int> CountMap; for(auto& e:words) { CountMap[e]++; } priority_queue<pair<string,int>,vector<pair<string,int>>,kv_pair> q(CountMap.begin(),CountMap.end()); vector<string> ret; for(size_t i=0;i<k;i++) { ret.push_back(q.top().first); q.pop(); } return ret; } }; 

结尾:

往期回顾:

C++11(上):重塑 C++ 的现代革命

结语:std::map 是 C++ 中兼具有序性和高效查找的关联容器,基于红黑树实现,核心优势是键唯一、自动排序、O (log n) 时间复杂度的增删改查,掌握 map 的用法,能让你在处理 “键值映射” 类问题时更高效、更简洁。根据实际场景选择 map 或 unordered_map,结合本文技巧,可大幅提升代码质量和开发效率

Read more

【GitHub项目推荐--Toonflow AI短剧工厂:一站式AI短剧创作平台】

简介 Toonflow AI短剧工厂是一个革命性的AI驱动短剧创作平台,由HBAI-Ltd团队开发。该项目致力于将小说文本智能转化为完整的短剧视频,实现从文字到影像的全流程自动化。通过集成先进的大语言模型、图像生成和视频合成技术,Toonflow让用户只需动动手指,就能将小说秒变剧集,创作效率提升10倍以上。 核心价值: * 全流程AI化:从文本到角色,从分镜到视频,0门槛完成短剧创作 * 效率革命:创作效率提升10倍+,大幅缩短制作周期 * 智能转换:自动将小说转化为结构化剧本和视觉内容 * 开源免费:基于AGPL-3.0许可证,完全开源且免费使用 技术定位:Toonflow填补了文学创作与影视制作之间的技术鸿沟。通过标准化的AI工作流,它为内容创作者提供了从创意到成品的完整解决方案,降低了视频制作的专业门槛。 主要功能 1. 智能角色生成 系统自动分析原始小说文本,智能识别并生成角色设定。生成内容包括角色的外貌特征、性格特点、身份背景等详细信息。为后续剧本创作和画面设计提供可靠的角色基础。支持批量角色生成,快速构建完整的角色库。 2. 自动化剧本生成 基

By Ne0inhk
2026年AI Agent实战:从玩具到生产力的落地手册(附源码)

2026年AI Agent实战:从玩具到生产力的落地手册(附源码)

欢迎文末添加好友交流,共同进步! “ 俺はモンキー・D・ルフィ。海贼王になる男だ!” * 前言 * 目录 * 一、AI Agent 的核心架构 * 1.1 什么是AI Agent? * 1.2 2026年Agent技术栈全景 * 二、从零搭建生产级Agent框架 * 2.1 项目结构设计 * 2.2 核心代码:Agent基类 * 2.3 记忆管理系统 * 三、三大核心技术实现 * 3.1 ReAct框架:推理+行动协同 * 3.2 工具调用系统 * 3.3 任务规划器 * 四、实战案例:智能客服Agent * 4.1 场景分析

By Ne0inhk
Flutter for OpenHarmony:Flutter 三方库 binarize — 鸿蒙应用的高效二进制数据序列化(适配鸿蒙 HarmonyOS Next ohos)

Flutter for OpenHarmony:Flutter 三方库 binarize — 鸿蒙应用的高效二进制数据序列化(适配鸿蒙 HarmonyOS Next ohos)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net。 Flutter for OpenHarmony:Flutter 三方库 binarize — 鸿蒙应用的高效二进制数据序列化(适配鸿蒙 HarmonyOS Next ohos) 前言 在鸿蒙(OpenHarmony)底层通讯与传感器数据处理中,高性能序列化至关重要。binarize 是一款高性能二进制序列化库,通过声明式布局将复杂的 Dart 对象直接映射为紧凑字节流。在构建鸿蒙 IoT 网关、音频流传输及高性能同步协议时,它是追求极致能效比的不二选择。 一、核心价值 1.1 基础概念 binarize 采用预定义布局(Layout)的模式进行读写。 鸿蒙高效数据交换 声明式 Layout 鸿蒙存储接口 NAPI/Socket Dart 对象数据 binarize 编码器

By Ne0inhk
鸿蒙 AI App 的技术架构解析

鸿蒙 AI App 的技术架构解析

子玥酱(掘金 / 知乎 / ZEEKLOG / 简书 同名) 大家好,我是子玥酱,一名长期深耕在一线的前端程序媛 👩‍💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚焦于业务型系统的工程化建设与长期维护。 我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括前端工程化、小程序、React / RN、Flutter、跨端方案, 在复杂业务落地、组件抽象、性能优化以及多端协作方面积累了大量真实项目经验。 技术方向:前端 / 跨端 / 小程序 / 移动端工程化 内容平台:掘金、知乎、ZEEKLOG、简书 创作特点:实战导向、源码拆解、少空谈多落地 文章状态:长期稳定更新,大量原创输出 我的内容主要围绕 前端技术实战、真实业务踩坑总结、框架与方案选型思考、行业趋势解读 展开。文章不会停留在“API 怎么用”,而是更关注为什么这么设计、在什么场景下容易踩坑、

By Ne0inhk