《C++ STL 关联式容器完全指南:深度解析 map 与 set 的使用》

《C++ STL 关联式容器完全指南:深度解析 map 与 set 的使用》

《C++ STL 关联式容器完全指南:深度解析 map 与 set 的使用》

📘 一、序列式容器 vs 关联式容器

1.1 什么是序列式容器?

我们之前学过的 stringvectorlistdequearrayforward_list 都属于序列式容器
它们的特点是:元素按照插入的物理顺序存储,逻辑结构是线性的,元素之间没有特别紧密的关联关系。

就像一列火车,车厢(元素)按顺序排列,你可以通过位置(下标或迭代器)直接访问。

1.2 什么是关联式容器?

关联式容器(如 mapsetmultimapmultiset)则是按关键字(key)来保存和访问数据的,逻辑结构通常是非线性的(如树或哈希表)。

  • set:只存储 key
  • map:存储 key-value 键值对
  • 它们底层通常使用红黑树(平衡二叉搜索树)实现,因此插入、删除、查找的时间复杂度都是 O(logN),并且遍历时元素是有序的(按 key 排序)。
就像字典,我们通过“单词”(key)查找“释义”(value),而不是通过第几页。

🧩 二、set 系列详解

2.1 set 的基本特性

  • 元素唯一(自动去重)
  • 默认按 key 升序排序(可通过仿函数改为降序)
  • 不允许修改 key(会破坏红黑树结构)
  • 底层为红黑树,支持双向迭代器
#include<set>#include<iostream>usingnamespace std;intmain(){ set<int> s ={5,2,8,2,9,1};for(int num : s){ cout << num <<" ";// 输出:1 2 5 8 9(自动去重+排序)}return0;}

2.2 重要接口与使用示例

✅ 构造方式
set<int> s1;// 默认构造 set<int>s2(s1.begin(), s1.end());// 迭代器区间构造 set<int> s3 ={1,2,3,4};// 初始化列表构造 set<int, greater<int>> s4;// 降序 set
✅ 插入与遍历
s.insert(10); s.insert({20,30,40});// 支持 initializer_list// 遍历(只读)for(auto it = s.begin(); it != s.end();++it){// *it = 100; // ❌ 错误!不能修改 key cout <<*it <<" ";}
✅ 查找与删除
auto pos = s.find(30);if(pos != s.end()){ s.erase(pos);// 通过迭代器删除} size_t num = s.erase(100);// 直接删除值,返回删除个数(0或1)// 使用 count 判断是否存在if(s.count(20)){ cout <<"20 exists"<< endl;}

2.3 multiset:允许重复元素的 set

multiset<int> ms ={5,2,5,1,5}; cout << ms.count(5);// 输出:3 ms.erase(5);// 删除所有值为 5 的元素
🔴 易错点multiset::erase(value) 会删除所有等于该值的元素,若只想删除一个,需先 find 获取迭代器再删除。

2.4 经典例题解析

📌 349. 两个数组的交集
vector<int>intersection(vector<int>& nums1, vector<int>& nums2){ set<int>s1(nums1.begin(), nums1.end()); set<int>s2(nums2.begin(), nums2.end()); vector<int> res;auto it1 = s1.begin(), it2 = s2.begin();while(it1 != s1.end()&& it2 != s2.end()){if(*it1 <*it2)++it1;elseif(*it1 >*it2)++it2;else{ res.push_back(*it1);++it1;++it2;}}return res;}
优点:利用 set 自动去重 + 有序,双指针遍历即可,时间复杂度 O(NlogN)。

📌 142. 环形链表 II(set 降维打击版)
ListNode *detectCycle(ListNode *head){ set<ListNode*> visited; ListNode* cur = head;while(cur){if(visited.count(cur))return cur; visited.insert(cur); cur = cur->next;}returnnullptr;}
优点:代码简洁,逻辑清晰,无需复杂数学证明,时间复杂度 O(NlogN),空间复杂度 O(N)。

🗺️ 三、map 系列详解

3.1 map 的基本特性

  • 存储键值对 pair<const Key, T>
  • key 唯一,按 key 排序
  • 支持通过 key 快速查找 value
  • 允许修改 value,不允许修改 key

3.2 pair 类型简介

pair<string,int> p1 ={"Alice",25}; cout << p1.first <<" "<< p1.second;// Alice 25auto p2 =make_pair("Bob",30);// 使用 make_pair 构造

3.3 重要接口与使用示例

✅ 构造与遍历
map<string,int> m ={{"Alice",90},{"Bob",85}};for(constauto& kv : m){ cout << kv.first <<": "<< kv.second << endl;}// 或使用迭代器for(auto it = m.begin(); it != m.end();++it){ cout << it->first <<": "<< it->second << endl;}
✅ 插入的四种方式
m.insert({"Charlie",88}); m.insert(make_pair("David",92)); m.insert(pair<string,int>("Eve",95)); m["Frank"]=70;// 最常用!若不存在则插入,存在则修改
🔴 易错点insert 时如果 key 已存在,不会覆盖原有 value,而 operator[] 会。

✅ operator[] 的三重功能
map<string,int> m; m["apple"]=10;// 1. 插入(key不存在) m["apple"]=20;// 2. 修改(key存在)int num = m["apple"];// 3. 查找(返回value,若不存在则插入默认值0)
内部实现简析

3.4 multimap:允许重复 key 的 map

  • 不支持 operator[](因为一个 key 可能对应多个 value)
  • find 返回第一个匹配的迭代器
  • count 返回 key 的出现次数
multimap<string,int> mm; mm.insert({"A",1}); mm.insert({"A",2}); cout << mm.count("A");// 输出:2

3.5 经典例题解析

📌 统计单词频率(map 的经典应用)
string arr[]={"apple","banana","apple","orange","banana","apple"}; map<string,int> countMap;// 方法1:find + insertfor(constauto& word : arr){auto it = countMap.find(word);if(it == countMap.end()){ countMap.insert({word,1});}else{ it->second++;}}// 方法2:operator[] 一行搞定for(constauto& word : arr){ countMap[word]++;}for(constauto& kv : countMap){ cout << kv.first <<": "<< kv.second << endl;}

📌 138. 随机链表的复制(map 映射法)
Node*copyRandomList(Node* head){ map<Node*, Node*> nodeMap; Node* cur = head;// 第一遍:建立原节点->新节点的映射while(cur){ nodeMap[cur]=newNode(cur->val); cur = cur->next;}// 第二遍:设置 next 和 random cur = head;while(cur){ nodeMap[cur]->next = nodeMap[cur->next]; nodeMap[cur]->random = nodeMap[cur->random]; cur = cur->next;}return nodeMap[head];}
优点:逻辑清晰,无需修改原链表,时间复杂度 O(N)。

📌 692. 前K个高频单词(map + 排序)
vector<string>topKFrequent(vector<string>& words,int k){ map<string,int> countMap;for(constauto& w : words) countMap[w]++; vector<pair<string,int>>vec(countMap.begin(), countMap.end());// 自定义排序:频率降序,频率相同时字典序升序sort(vec.begin(), vec.end(),[](constauto& a,constauto& b){return a.second > b.second ||(a.second == b.second && a.first < b.first);}); vector<string> res;for(int i =0; i < k;++i) res.push_back(vec[i].first);return res;}

🔄 四、有序 vs 无序容器

特性set/map(有序)unordered_set/unordered_map(无序)
底层结构红黑树哈希表
时间复杂度O(logN)平均 O(1),最坏 O(N)
是否有序是(按 key 排序)
是否需要哈希函数
迭代器稳定性稳定不稳定(重组时失效)
📌 选择建议:如果需要有序遍历,选 set/map如果只关心存在性/快速查找,且不关心顺序,选 unordered_xxx如果 key 是自定义类型,使用 unordered_xxx 需自定义哈希函数

💎 五、核心总结与易错点

✅ 必须掌握的重点:

  1. set 用于去重排序,map 用于键值映射
  2. operator[] 在 map 中具有插入、修改、查找三重功能
  3. 关联式容器的迭代器是双向迭代器,支持 ++--
  4. 红黑树保证操作时间复杂度为 O(logN),且遍历有序

❌ 常见易错点:

  1. 试图修改 set 或 map 的 key → 编译错误
  2. 误以为 map::insert 会覆盖 value → 实际上不会
  3. 对 multimap 使用 operator[] → 编译错误(不支持)
  4. 忽略 unordered_xxx 的最坏时间复杂度 → 哈希冲突可能导致 O(N)
  5. 遍历时删除元素未更新迭代器 → 可能导致未定义行为

🚀 六、进阶练习建议

  1. 尝试用 map 实现一个简单的电话簿系统(姓名→电话)
  2. set 实现论文查重中的停用词过滤
  3. 结合 priority_queuemap 实现实时 TopK 统计
  4. 自定义一个类作为 map 的 key,实现比较运算符或哈希函数

📖 学习资源推荐cplusplus.com set参考cplusplus.com map参考《STL源码剖析》—— 侯捷LeetCode 标签:哈希表、设计

Read more

Flutter for OpenHarmony: Flutter 三方库 envied_generator 给鸿蒙应用的敏感 API Key 穿上“不可破解”的防护服(安全性加固利器)

Flutter for OpenHarmony: Flutter 三方库 envied_generator 给鸿蒙应用的敏感 API Key 穿上“不可破解”的防护服(安全性加固利器)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在进行 OpenHarmony 应用开发时,我们不可避免地要集成各种三方服务(如高德地图 KEY、Firebase Secret、或是鸿蒙分布式服务的授权 Token)。如果你直接将这些字符串写在 Dart 代码里,任何初级黑客都能通过反编译你的 HAP 包,轻松获取这些敏感资产,导致巨大的商业损失。 envied_generator 配合 envied 就是专门解决这一安全痛点的。它不仅能将配置从 .env 文件读取到代码中,更关键的是它支持 Obfuscate(代码混淆)。它将你的 Key 转化为一串复杂的位运算逻辑,让反编译后的结果变得面目全非,为鸿蒙应用的资产安全筑起第一道堤坝。 一、配置加固工作流模型 该库通过代码生成,将明文配置文件转化为混淆后的 Dart 类。 .env (敏感明文) envied_generator

By Ne0inhk
Flutter for OpenHarmony:Flutter 三方库 money2 — 坚不可摧的鸿蒙金融核心组件

Flutter for OpenHarmony:Flutter 三方库 money2 — 坚不可摧的鸿蒙金融核心组件

欢迎加入开源鸿蒙跨平台社区:开源鸿蒙跨平台开发者社区 前言 如果您正在开发的 Flutter for OpenHarmony 应用涉及金融核算、商城交易或任何带有财务账单的业务,那么对金额的精确处理将极其关键。 在传统开发中,如果直接使用系统基础的 Double 类型进行财务计算(例如 0.1 + 0.2 会变成 0.30000000000000004),极易导致对账失败,严重时甚至会引发系统性的财务灾难。 money2 这个开源组件正是为了防止这种浮点运算精度丢失而生。它在底层基于大整数操作结合位移来处理金额金额,从而绝对保证在进行复杂的金融计算时,不会丢失哪怕一丝一毫的精度。 一、原理解析 / 概念介绍 1.1 基础概念 money2 绝不仅仅是一堆简单的加减工具函数。其核心思想是使用大整数来表示货币的最小面值单位。例如 1.25 美元,它在底层对象中实际被安全地存储为代表分的大整数 125 和指数 -2。这里面完全规避了极其危险的浮点操作。 系统原始 1.2

By Ne0inhk
Flutter 三方库 metadata_fetch_plus 的鸿蒙化适配指南 - 实现极速的网页元数据提取与 Open Graph 协议解析、支持端侧富文本链接预览渲染实战

Flutter 三方库 metadata_fetch_plus 的鸿蒙化适配指南 - 实现极速的网页元数据提取与 Open Graph 协议解析、支持端侧富文本链接预览渲染实战

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 metadata_fetch_plus 的鸿蒙化适配指南 - 实现极速的网页元数据提取与 Open Graph 协议解析、支持端侧富文本链接预览渲染实战 前言 在进行 Flutter for OpenHarmony 的社交媒体、新闻资讯或即时通讯类应用开发时,如何根据用户分享的一个单薄的 URL,自动生动地展示出其对应的网页标题、封面图及描述信息?metadata_fetch_plus 是专为网页语义数据抓取设计的利器。它深度支持 Open Graph, Twitter Cards, Scheme.org 等主流元数据协议。本文将探讨如何在鸿蒙端构建极致的链接预览体验。 一、原直观解析 / 概念介绍 1.1 基础原理 该库建立在高效的 HTML 语义解析逻辑之上。

By Ne0inhk
Flutter 三方库 nordigen_integration 的鸿蒙化适配指南 - 安全接入全球金融数据、处理 OAuth2 开放银行协议及账户集成实战

Flutter 三方库 nordigen_integration 的鸿蒙化适配指南 - 安全接入全球金融数据、处理 OAuth2 开放银行协议及账户集成实战

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 nordigen_integration 的鸿蒙化适配指南 - 安全接入全球金融数据、处理 OAuth2 开放银行协议及账户集成实战 前言 随着全球金融数字化的浪潮,个人财务管理(PFM)和开放银行(Open Banking)应用正以前所未有的速度渗透进我们的生活。在欧洲,PSD2 协议的强制推行使得开发者可以通过标准化的 API 安全地访问成千上万家银行的账户数据。 nordigen_integration 正是这一领域的佼佼者,它极简地封装了 GoCardless(原 Nordigen)的复杂 API,让开发者只需几行代码即可完成银行授权和交易拉取。 当我们将这类高安全、高合规性的应用适配到 OpenHarmony 平台时,隐私数据的隔离保护、OAuth2 的安全重定向以及跨国界的数据一致性成为了新的挑战。本文将为你详解如何在鸿蒙生态中构建一条通往全球银行系统的“数字专线”。 一、原理解析 / 概念介绍

By Ne0inhk