当前位置: 首页 > article >正文

吃透C++ STL map/set:从入门到实战,新手也能轻松上手

文章目录前言一、先搞懂map和set是什么核心区别在哪二、set使用详解去重排序一键搞定三、map使用详解键值映射高效查找四、map和set的常见避坑点新手必看五、map/set vs 其他容器怎么选六、总结前言在C STL标准模板库中map和set是最常用的关联式容器它们底层基于红黑树一种平衡二叉搜索树实现天生具备“自动排序”和“高效检索”的特性能极大简化我们的代码开发尤其在需要快速查找、去重、排序的场景中堪称“神器”。很多新手刚接触map和set时常常混淆两者的用途什么时候用set什么时候用map它们的接口有什么区别今天这篇博客全程以“实用”为核心不搞复杂理论只讲“怎么用、用在哪、避坑点”搭配极简实战代码新手跟着敲一遍就能轻松掌握map和set的核心用法。一、先搞懂map和set是什么核心区别在哪map和set同属关联式容器底层都是红黑树因此都具备O(log₂N)的插入、查找、删除效率且元素会自动排序但两者的核心用途和存储结构完全不同这是区分它们的关键。1. 核心定义与存储结构set集合仅存储“键key”不存储“值value”且不允许重复key元素会按照key的默认规则升序自动排序。可以理解为“去重排序”的容器比如存储一个班级的学号不重复且需要有序。map映射存储“键值对key-value”key唯一value可重复元素会按照key的默认规则升序自动排序。可以理解为“字典”key是“单词”value是“释义”通过key能快速找到对应的value比如存储学生的学号key和姓名value。2. 核心区别一张表看懂特性setmap存储内容仅key键key-value键值对key是否唯一是不允许重复是不允许重复排序依据按key自动排序按key自动排序核心用途去重、排序、快速查找某个值是否存在键值映射、通过key快速获取value、排序底层结构红黑树平衡二叉搜索树红黑树平衡二叉搜索树补充STL还提供了multiset和multimap允许key重复用法和set、map基本一致仅在“key唯一性”上有差异本文重点讲解最常用的set和mapkey唯一。二、set使用详解去重排序一键搞定set的用法非常简单核心围绕“插入、查找、删除、遍历”四个操作无需手动排序插入后元素自动按升序排列且自动去重。1. 头文件与定义使用set必须包含头文件set定义时需指定存储的数据类型即key的类型默认排序规则为升序#include iostream #include set // set的头文件 using namespace std; // 定义set存储int类型默认升序 setint s; // 可选定义降序set需指定排序规则 setint, greaterint s_desc; // 降序排列2. 核心接口实战常用必掌握set的接口不多重点掌握以下6个就能应对大部分场景搭配代码示例理解更直观1插入元素insert()插入单个元素自动去重、自动排序也可插入区间内的元素。setint s; // 插入单个元素 s.insert(5); s.insert(2); s.insert(8); s.insert(2); // 重复元素插入失败不会报错 // 插入区间元素从数组插入 int arr[] {3, 7, 1}; s.insert(arr, arr 3); // 此时set中的元素1, 2, 3, 5, 7, 8自动升序、去重2查找元素find()查找指定key是否存在返回迭代器找到则返回对应元素的迭代器未找到则返回s.end()set的末尾迭代器指向元素后面的位置。setint s {1, 2, 3, 5, 7, 8}; // 查找key5 auto it s.find(5); if (it ! s.end()) { cout 找到元素 *it endl; // 输出找到元素5 } else { cout 未找到元素 endl; } // 查找key4 it s.find(4); if (it s.end()) { cout 未找到元素 endl; // 输出未找到元素 }3删除元素erase()有三种删除方式按迭代器删除、按key删除、删除区间内元素最常用的是前两种。setint s {1, 2, 3, 5, 7, 8}; // 1. 按key删除 s.erase(3); // 删除key3的元素返回删除的个数0或1 // 2. 按迭代器删除先找到元素再删除 auto it s.find(5); if (it ! s.end()) { s.erase(it); // 删除key5的元素 } // 3. 删除区间元素删除从begin到end的所有元素即清空set // s.erase(s.begin(), s.end()); // 此时set中的元素1, 2, 7, 84遍历元素迭代器/范围forset支持迭代器遍历和C11后的范围for遍历遍历顺序就是元素的排序顺序默认升序。setint s {1, 2, 7, 8}; // 1. 迭代器遍历 for (setint::iterator it s.begin(); it ! s.end(); it) { cout *it ; // 输出1 2 7 8 } cout endl; // 2. 范围for遍历C11及以上更简洁 for (auto num : s) { cout num ; // 输出1 2 7 8 } cout endl;5获取元素个数size()setint s {1, 2, 7, 8}; cout set元素个数 s.size() endl; // 输出46清空元素clear()setint s {1, 2, 7, 8}; s.clear(); // 清空所有元素 cout 清空后元素个数 s.size() endl; // 输出03. set实战场景去重排序最典型的场景对一组数据去重并排序无需手动写排序和去重逻辑set一键搞定。#include iostream #include set using namespace std; int main() { // 一组重复、无序的数据 int arr[] {5, 2, 8, 2, 3, 7, 1, 5, 9}; int n sizeof(arr) / sizeof(arr[0]); // 用set去重排序 setint s(arr, arr n); // 输出去重排序后的结果 cout 去重排序后; for (auto num : s) { cout num ; // 输出1 2 3 5 7 8 9 } return 0; }三、map使用详解键值映射高效查找map的核心是“key-value”映射key唯一且排序通过key能快速找到对应的value用法和set类似但多了对value的操作重点掌握“键值对的插入、访问、修改”。1. 头文件与定义使用map必须包含头文件map定义时需指定key和value的类型默认按key升序排序#include iostream #include map // map的头文件 using namespace std; // 定义mapkey为int学号value为string姓名默认升序 mapint, string m; // 可选定义降序map mapint, string, greaterint m_desc; // 按key降序排列2. 核心接口实战常用必掌握map的接口和set有很多相似之处但增加了对value的操作重点掌握以下7个1插入键值对insert() / 下标[]有两种常用插入方式insert()插入pair对象下标[]直接赋值更简洁注意下标[]若key不存在会自动插入该keyvalue为默认值如string为空串。mapint, string m; // 方式1insert()插入pair对象推荐可判断是否插入成功 m.insert(pairint, string(101, 张三)); m.insert(make_pair(102, 李四)); // make_pair更简洁 // 插入重复key返回pair迭代器, boolbool为false表示插入失败 auto ret m.insert(make_pair(101, 王五)); if (!ret.second) { cout key101已存在插入失败 endl; } // 方式2下标[]插入简洁但会自动创建不存在的key m[103] 赵六; // 插入key103value赵六 m[104] 孙七; // 此时map中的键值对按key升序(101,张三), (102,李四), (103,赵六), (104,孙七)2访问value下标[] / find() 迭代器两种访问方式下标[]更简洁但要注意若key不存在会自动插入该keyvalue为默认值find()更安全不会自动插入。mapint, string m {{101, 张三}, {102, 李四}, {103, 赵六}}; // 方式1下标[]访问简洁 cout 101号学生 m[101] endl; // 输出101号学生张三 cout 104号学生 m[104] endl; // key104不存在自动插入value为空串输出空 // 方式2find() 迭代器安全推荐 auto it m.find(102); if (it ! m.end()) { cout 102号学生 it-second endl; // 输出102号学生李四 } // 访问不存在的key不会自动插入 it m.find(105); if (it m.end()) { cout 105号学生不存在 endl; }注意map的迭代器指向的是“键值对pair”通过it-first访问keyit-second访问value。3修改value下标[] / find() 迭代器修改value的前提是key已存在两种方式均可下标[]更简洁。mapint, string m {{101, 张三}, {102, 李四}}; // 方式1下标[]修改 m[101] 张三丰; // 将101号的value改为张三丰 // 方式2find() 迭代器修改 auto it m.find(102); if (it ! m.end()) { it-second 李世民; // 将102号的value改为李世民 } // 输出修改后的值 cout 101号 m[101] endl; // 输出101号张三丰 cout 102号 m[102] endl; // 输出102号李世民4删除元素erase()和set用法一致支持按key删除、按迭代器删除、删除区间元素。mapint, string m {{101, 张三}, {102, 李四}, {103, 赵六}}; // 1. 按key删除 m.erase(102); // 删除key102的键值对 // 2. 按迭代器删除 auto it m.find(103); if (it ! m.end()) { m.erase(it); // 删除key103的键值对 } // 3. 删除区间元素清空map // m.erase(m.begin(), m.end()); // 此时map中仅剩余(101, 张三)5遍历元素迭代器/范围for遍历顺序按key的排序规则重点关注键值对的访问方式。mapint, string m {{101, 张三}, {102, 李四}, {103, 赵六}}; // 1. 迭代器遍历 for (mapint, string::iterator it m.begin(); it ! m.end(); it) { // it-first是keyit-second是value cout 学号 it-first 姓名 it-second endl; } // 2. 范围for遍历C11及以上 for (auto pair : m) { cout 学号 pair.first 姓名 pair.second endl; }6获取元素个数size()7清空元素clear()和set用法完全一致直接调用接口即可。mapint, string m {{101, 张三}, {102, 李四}}; cout 元素个数 m.size() endl; // 输出2 m.clear(); cout 清空后个数 m.size() endl; // 输出03. map实战场景键值映射最典型的场景存储键值对通过key快速查找value比如存储学生学号与成绩、单词与释义等。#include iostream #include map #include string using namespace std; int main() { // 存储学生学号key和成绩value mapint, int student_score; // 插入数据 student_score.insert(make_pair(101, 95)); student_score.insert(make_pair(102, 88)); student_score[103] 92; student_score[104] 79; // 查找并输出102号学生的成绩 auto it student_score.find(102); if (it ! student_score.end()) { cout 102号学生成绩 it-second endl; // 输出88 } // 修改104号学生的成绩 student_score[104] 85; cout 修改后104号学生成绩 student_score[104] endl; // 输出85 // 遍历所有学生成绩按学号升序 cout 所有学生成绩 endl; for (auto pair : student_score) { cout 学号 pair.first 成绩 pair.second endl; } return 0; }四、map和set的常见避坑点新手必看误区1map的下标[]会自动插入不存在的key——这是新手最容易踩的坑如果只是想“查找”value不要用下标[]用find()更安全避免误插入多余的key。误区2set和map可以直接修改元素——不可以set的元素是const类型不能修改否则会破坏排序和唯一性map只能修改value不能修改key修改key会破坏排序和唯一性。误区3插入重复key会报错——不会报错set和map插入重复key时插入会失败但程序不会崩溃可通过insert()的返回值判断是否插入成功。误区4set和map的迭代器可以随意加减——不可以它们的迭代器是“双向迭代器”只能用、--操作不能用1、-1和vector的随机迭代器不同。误区5底层是红黑树所以插入效率一定很高——红黑树插入时会自动平衡效率是O(log₂N)但比vector的尾插O(1)慢适合“频繁查找、删除”的场景不适合“频繁尾插”的场景。五、map/set vs 其他容器怎么选很多新手不知道什么时候用map/set什么时候用vector/list这里给出明确的选择依据需要去重、排序优先用set需要键值映射、快速通过key找value优先用map需要频繁随机访问、尾插尾删优先用vector需要频繁插入、删除非尾端且不需要排序优先用list需要允许key重复用multiset/multimap用法和set/map基本一致。六、总结map和set是C STL中最实用的关联式容器核心优势在于“自动排序”和“O(log₂N)的高效操作”底层红黑树的实现让它们无需我们手动管理排序和平衡极大提升开发效率。对于新手来说重点掌握1. 区分map和setset存key去重排序map存key-value键值映射2. 核心接口insert()、find()、erase()、size()、clear()以及map的下标访问3. 避坑点map下标[]的自动插入、不可修改set的元素和map的key。其实map和set的用法并不复杂多敲几遍实战代码熟悉接口的使用场景就能轻松掌握。它们在面试和实际开发中都非常常用学好它们能让你的代码更简洁、高效、易维护。

相关文章:

吃透C++ STL map/set:从入门到实战,新手也能轻松上手

文章目录 前言 一、先搞懂:map和set是什么?核心区别在哪? 二、set使用详解:去重排序,一键搞定 三、map使用详解:键值映射,高效查找 四、map和set的常见避坑点(新手必看&#xff…...

Dify插件开发实战:Python SDK快速构建AI工作流扩展工具

1. 项目概述与核心价值如果你正在为 Dify 构建自定义插件,并且厌倦了从零开始处理复杂的协议、序列化和生命周期管理,那么langgenius/dify-plugin-sdks这个项目就是你一直在找的“脚手架”。简单来说,它是一套官方维护的软件开发工具包&#…...

私有化部署ChatGPT Web应用:从架构解析到实战部署指南

1. 项目概述与核心价值最近在折腾一个挺有意思的开源项目,叫“ChatGPTwebV15”。这名字听起来有点技术范儿,但说白了,就是一个让你能自己部署、完全掌控的类ChatGPT网页应用。它基于OpenAI的API,但把整个交互界面、对话管理、甚至…...

如何在手机上3步完成Android内核刷入:Horizon Kernel Flasher终极指南

如何在手机上3步完成Android内核刷入:Horizon Kernel Flasher终极指南 【免费下载链接】HorizonKernelFlasher A simple app that can flash AnyKernel flashable zips on android 项目地址: https://gitcode.com/gh_mirrors/ho/HorizonKernelFlasher 还在为…...

2026.5.7@霖宇博客制作中遇见的问题

倒数2个知识点没看 记得看下1. one 前端网页的验证码如何修改为后端的验证码 将前端生成的验证码修改为后端生成,核心目的是为了解决安全性问题。如果验证码只在前端生成和校验,恶意攻击者可以轻松绕过登录页面直接发起请求,导致验证码完全失…...

国家医疗保障webpack

开始逆向 定位到signature的位置 发现是webpack模块 要找到入口位置 这一块是加载器的位置 先把这个扣下来 再扣自执行函数 不要里面的函数 会报环境错误 然后报这个错误 把加载器函数调用注释 o函数挂载到全局去调用 代码全拉 然后注释掉 这一块是 类ob 然后报t is not define…...

C语言实现精简Smalltalk运行时:探索面向对象与消息传递的本质

1. 项目概述:当“小结构”遇上“小对话”如果你在开源社区里混迹过一段时间,可能会发现一个有趣的现象:很多项目的名字,乍一看不知所云,但一旦你理解了它的设计哲学,就会觉得无比贴切。tinystruct/smalltal…...

终极健康办公指南:Stretchly科学休息管理工具完全解析

终极健康办公指南:Stretchly科学休息管理工具完全解析 【免费下载链接】stretchly The break time reminder app 项目地址: https://gitcode.com/gh_mirrors/st/stretchly 在数字时代,长时间面对电脑屏幕已成为现代职场人士的日常。Stretchly作为…...

WarcraftHelper:魔兽争霸III终极优化工具3步快速配置指南

WarcraftHelper:魔兽争霸III终极优化工具3步快速配置指南 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 还在为魔兽争霸III在现代电脑上运…...

AI智能体驱动TDD:agent-flow-tdd框架实战与优化指南

1. 项目概述:当AI智能体遇上测试驱动开发 如果你和我一样,在软件开发领域摸爬滚打了十几年,肯定对测试驱动开发(TDD)又爱又恨。爱的是它那套“红-绿-重构”的严谨流程,确实能产出健壮、可维护的代码&#x…...

WarcraftHelper:魔兽争霸3终极兼容性修复与性能增强方案

WarcraftHelper:魔兽争霸3终极兼容性修复与性能增强方案 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 还在为魔兽争霸3在现代电脑上运行…...

蓝桥杯单片机决赛避坑指南:从“高位熄灭”到“双键长按”的实战代码优化

蓝桥杯单片机决赛代码优化实战:从数码管显示到双键检测的进阶技巧 参加蓝桥杯单片机竞赛的同学们都知道,决赛环节往往会在基础功能上设置诸多"陷阱",考验选手对细节的掌控能力。本文将针对数码管高位熄灭、温度传感器小数处理、双键…...

前端响应式设计:最佳实践

前端响应式设计:最佳实践 前言 响应式设计是现代前端开发的核心概念之一,它确保网站和应用在不同设备上都能提供良好的用户体验。随着移动设备的普及,响应式设计变得越来越重要。今天,我就来给大家讲讲响应式设计的最佳实践&#…...

CUDA内核优化:从手工调优到AI驱动的自动化实践

1. CUDA内核优化:从手工调优到AI驱动的范式转变在GPU计算领域,CUDA内核优化一直是提升性能的关键手段。传统优化方法高度依赖工程师对GPU架构的深入理解,需要手动调整内存访问模式、线程块配置等参数。这种人工优化过程通常耗时数周甚至数月&…...

AI代码审查实战:基于LLM的自动化代码质量提升方案

1. 项目概述与核心价值最近在GitHub上看到一个挺有意思的项目,叫abczsl520/codex-review。光看名字,可能有点摸不着头脑,codex这个词在技术圈里通常和OpenAI的Codex模型有关,而review又指向了代码审查。所以,这个项目大…...

ZYNQ裸机双网口实战:手把手教你修改LWIP库以支持KSZ9031 PHY与EMIO配置

ZYNQ裸机双网口实战:LWIP库深度改造与KSZ9031 PHY适配全指南 1. 项目背景与核心挑战 在工业控制、边缘计算等场景中,ZYNQ SoC凭借其PSPL的异构架构优势,常被用于构建高性能网络设备。当标准单网口无法满足需求时,通过PL扩展EMIO实…...

内存级向量检索库memsearch:原理、实战与性能调优

1. 项目概述:向量检索的“内存级”加速方案最近在折腾RAG(检索增强生成)应用时,向量数据库的检索延迟成了性能瓶颈。尤其是在处理高并发、低延迟的在线服务场景,即使是最优的索引,一次检索也常常需要几十到…...

Arm DS开发环境与处理器优化实战指南

1. Arm DS开发环境与处理器优化基础在嵌入式系统和移动计算领域,Arm架构凭借其出色的能效比和可扩展性,已成为主流处理器设计。作为开发者,我们经常面临如何在特定硬件上榨取最大性能的挑战。Arm Development Studio(简称Arm DS&a…...

使用 Taotoken 前后在管理多个 API Key 与监控用量方面的效率对比感受

使用 Taotoken 前后在管理多个 API Key 与监控用量方面的效率对比感受 1. 引言:多模型接入带来的管理挑战 在项目开发中引入大模型能力,往往意味着需要同时对接多个不同的模型服务商。每个服务商都有独立的控制台、独立的 API Key 管理体系以及独立的账…...

OpenClaw实战案例库:AI智能体应用模式与工程实践指南

1. 项目概述:一个为OpenClaw而生的真实案例宝库如果你正在探索OpenClaw,或者已经用它搭建了一些自动化流程,但总觉得“别人到底是怎么玩的?”、“有没有更高级的用法可以参考?”,那么你找对地方了。awesome…...

AI协同开发新范式:基于规范驱动的Agentic Workflows实践

1. 项目概述:告别碎片化,用“活的”规范驱动AI协同开发如果你和我一样,每天都在跟Claude Code、Cursor这类AI编程工具打交道,那你肯定也经历过这种痛苦:想实现一个复杂功能,得先花十几分钟给AI解释一遍项目…...

macOS Catalina Patcher:让老旧Mac重获新生的神奇工具

macOS Catalina Patcher:让老旧Mac重获新生的神奇工具 【免费下载链接】macos-catalina-patcher macOS Catalina Patcher (http://dosdude1.com/catalina) 项目地址: https://gitcode.com/gh_mirrors/ma/macos-catalina-patcher 还在为你的老款Mac无法升级到…...

ARM Cortex-A9 MMU架构与TLB优化实践

1. ARM Cortex-A9 MMU架构概述在嵌入式系统开发中,内存管理单元(MMU)是实现虚拟内存系统的核心组件。ARM Cortex-A9处理器的MMU基于ARMv7-A架构,采用了两级TLB(Translation Lookaside Buffer)结构来加速虚拟…...

基于MCP协议构建AI侍酒师:原理、配置与实战指南

1. 项目概述:为AI助手注入侍酒师灵魂 如果你和我一样,既是个技术爱好者,又对美食美酒有点追求,那你肯定遇到过这样的场景:周末想在家做顿大餐,打开冰箱看着一堆食材,却完全不知道该配什么酒。问…...

给停车场系统加点“声光特效”:Java整合海康车牌识别机的语音播报与LED屏

智能停车场中的声光交互:Java深度整合海康设备实战 当一辆车缓缓驶入现代智能停车场,LED屏幕上实时显示的车牌号和欢迎语,配合清晰的语音提示,这种无缝的交互体验背后是硬件与软件的精妙协作。作为开发者,我们不仅要实…...

量子纠错协议在多量子比特系统中的性能优化研究

1. 量子纠错协议在多量子比特系统中的性能增益研究 量子计算领域近年来取得了显著进展,但量子比特的脆弱性仍然是实现实用化量子计算机的主要障碍。量子纠错(QEC)作为解决这一问题的关键技术,其核心思想是通过冗余编码来保护量子信息免受噪声影响。本文将…...

99AI全栈框架解析:从开源模型到可交付AI应用的工程实践

1. 项目概述:当开源模型遇上“99AI”,一个全栈AI应用的新范式最近在GitHub上看到一个挺有意思的项目,叫“vastxie/99AI”。光看名字,你可能会觉得这又是一个蹭AI热点的玩具项目,或者是一个简单的模型调用封装。但当我点…...

终极指南:如何使用VirtualRouter将Windows电脑变成免费无线热点

终极指南:如何使用VirtualRouter将Windows电脑变成免费无线热点 【免费下载链接】VirtualRouter Wifi Hotspot for Windows computers (Windows 7, 8.x, Server 2012 and newer!) 项目地址: https://gitcode.com/gh_mirrors/vi/VirtualRouter 你是否曾为酒店…...

DM6446平台JPEG编解码开发环境搭建与优化

1. DM6446平台JPEG编解码开发环境搭建在嵌入式视频处理领域,TMS320DM6446作为TI经典的DaVinci系列处理器,凭借其双核架构(ARM9DSP)和丰富的视频外设接口,成为早期视频监控、流媒体设备的首选方案。我曾在多个工业视觉项…...

本地部署多AI账号智能管理工具CodexPool:实现自动轮换与用量监控

1. 项目概述:一个面向开发者的多账号智能管理工具 如果你同时管理着多个不同平台的AI服务账号,比如OpenAI的ChatGPT、Google的Gemini或者Anthropic的Claude,那么你肯定体会过那种在浏览器标签页、终端窗口和一堆 auth.json 文件之间来回切…...