跳到主要内容C++ STL 进阶:set 与 map 容器使用详解 | 极客日志C++算法
C++ STL 进阶:set 与 map 容器使用详解
STL 关联式容器主要包括 set、map 及其多版本。set 保证元素唯一且有序,map 提供键值映射。pair 辅助存储成对数据。核心操作涵盖构造、插入、删除、查找及范围查询。结合 LeetCode 实战案例,展示如何利用容器特性解决数组交集、链表环检测及词频统计问题。
橘子海1 浏览 C++ STL 进阶:set 与 map 容器使用详解
容器分类概览
STL 中的容器主要分为序列式容器和关联式容器。序列式容器(如 vector、list)强调元素的线性存储顺序,通过位置索引或迭代器访问;而关联式容器(如 set、map)则基于树或哈希表实现,通过关键字(Key)进行高效检索。
| 维度 | 序列式容器 | 关联式容器 |
|---|
| 逻辑结构 | 线性序列 | 非线性结构(树、哈希表) |
| 元素关联 | 仅通过位置顺序关联 | 通过键值的有序性或哈希映射关联 |
| 访问依据 | 存储位置(索引或迭代器) | 关键字(Key) |
| 典型实现 | vector, list, deque | set, map, unordered_set |
set 容器详解
std::set 是关联式容器的一种,内部通常由红黑树实现。它有两个核心特性:自动排序和元素唯一性。插入重复元素时会被自动忽略。
模板参数
template<class T, class Compare = std::less<T>, class Alloc = std::allocator<T>>
class set;
T: 元素类型,需支持比较操作。
Compare: 比较器,默认按升序排列。
Alloc: 内存分配器。
构造与初始化
#include <iostream>
#include <set>
using namespace std;
{
set<> s1;
myints[] = {, , , , };
;
;
set<, greater<>> s4;
;
}
int
main
()
int
int
10
20
30
40
50
set<int> s2(myints, myints + 5)
set<int> s3(s2)
int
int
return
0
核心操作
容量管理
size(): 返回元素个数。
empty(): 判断是否为空。
clear(): 清空所有元素。
修改操作
insert(val): 插入元素,返回 pair<iterator, bool>,第二个值表示是否插入成功。
erase(pos): 删除指定迭代器位置的元素。
erase(key): 删除指定键值的元素,返回删除数量。
swap(other): 交换两个容器的内容。
set<int> s;
s.insert(10);
s.insert(10);
auto it = s.find(10);
if (it != s.end()) s.erase(it);
查找与遍历
find(key): 返回指向该元素的迭代器,未找到返回 end()。
count(key): 在 set 中只能返回 0 或 1。
lower_bound(key): 返回第一个不小于 key 的迭代器。
upper_bound(key): 返回第一个大于 key 的迭代器。
equal_range(key): 同时返回 lower_bound 和 upper_bound 的范围。
set<int> s = {10, 20, 30, 40, 50};
auto itlow = s.lower_bound(20);
auto itup = s.upper_bound(40);
s.erase(itlow, itup);
实战代码:去重与遍历
#include <iostream>
#include <set>
#include <string>
using namespace std;
int main() {
set<int> s = {5, 2, 7, 2, 8, 5, 9};
s.insert({2, 8, 3, 9});
set<string> strset = {"sort", "insert", "add"};
for (const auto& e : strset) {
cout << e << " ";
}
return 0;
}
multiset 容器
multiset 与 set 类似,但允许元素重复。当插入重复值时,它会保留所有副本并按序排列。
- 区别:
set 元素唯一,multiset 可重复。
- 查找:
find 返回第一个匹配项,需配合循环才能获取所有重复项。
- 计数:
count 返回实际出现的次数。
- 删除:
erase(key) 会删除所有匹配的元素。
#include <iostream>
#include <set>
using namespace std;
int main() {
multiset<int> ms = {4, 2, 7, 2, 4, 8};
int x = 2;
cout << "Count: " << ms.count(x) << endl;
auto pos = ms.find(x);
while (pos != ms.end() && *pos == x) {
cout << *pos << " ";
++pos;
}
return 0;
}
pair 类模板
std::pair 用于将两个不同类型的数据组合成一个逻辑单元,常用于 map 的键值对存储。
#include <utility>
#include <iostream>
using namespace std;
int main() {
pair<string, int> p("apple", 10);
cout << p.first << ": " << p.second << endl;
auto p2 = make_pair(1, "hello");
return 0;
}
map 容器详解
std::map 存储键值对(key-value),键(Key)必须唯一且有序,值(Value)可以任意。
模板参数
template<class Key, class T, class Compare = less<Key>, class Alloc = allocator<pair<const Key, T>>>
class map;
访问方式
- 下标运算符
[]: 如果键不存在,会自动创建并初始化为默认值。适合插入或修改。
- 迭代器/
find: 更安全,不会隐式插入元素。
#include <iostream>
#include <map>
using namespace std;
int main() {
map<string, string> dict;
dict["left"] = "左边";
dict.insert(make_pair("right", "右边"));
if (dict.count("left")) {
cout << dict["left"] << endl;
}
return 0;
}
核心操作对比
find(k): 返回迭代器,未找到为 end()。
insert(p): 返回 pair<iterator, bool>。
erase(k): 删除指定键,返回删除数量。
operator[]: 兼具查找和插入功能。
map<string, int> countMap;
string arr[] = {"apple", "banana", "apple"};
for (const auto& str : arr) {
countMap[str]++;
}
multimap 容器
multimap 允许键重复,适用于一对多的映射关系(如一个学生对应多门课程)。它不支持 operator[],因为无法明确指定修改哪一个重复键对应的值。
OJ 实战演练
1. 两个数组的交集 (LeetCode 349)
class Solution {
public:
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> ret;
auto it1 = s1.begin();
auto it2 = s2.begin();
while (it1 != s1.end() && it2 != s2.end()) {
if (*it1 < *it2) {
it1++;
} else if (*it1 > *it2) {
it2++;
} else {
ret.push_back(*it1);
it1++; it2++;
}
}
return ret;
}
};
2. 环形链表 II (LeetCode 142)
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
set<ListNode*> visited;
ListNode* cur = head;
while (cur) {
auto ret = visited.insert(cur);
if (!ret.second) return cur;
cur = cur->next;
}
return nullptr;
}
};
3. 随机链表的复制 (LeetCode 138)
class Solution {
public:
Node* copyRandomList(Node* head) {
map<Node*, Node*> m;
Node* copyHead = nullptr;
Node* copyTail = nullptr;
Node* curr = head;
while (curr) {
if (!copyTail) {
copyHead = copyTail = new Node(curr->val);
} else {
copyTail->next = new Node(curr->val);
copyTail = copyTail->next;
}
m[curr] = copyTail;
curr = curr->next;
}
curr = head;
Node* copy = copyHead;
while (curr) {
copy->random = curr->random ? m[curr->random] : nullptr;
curr = curr->next;
copy = copy->next;
}
return copyHead;
}
};
4. 前 K 个高频单词 (LeetCode 692)
结合 map 统计频率,priority_queue 或 sort 排序。
class Solution {
public:
struct Compare {
bool operator()(const pair<string, int>& x, const pair<string, int>& y) const {
return x.second < y.second || (x.second == y.second && x.first > y.first);
}
};
vector<string> topKFrequent(vector<string>& words, int k) {
map<string, int> m;
for (const auto& w : words) m[w]++;
priority_queue<pair<string, int>, vector<pair<string, int>>, Compare> pq(m.begin(), m.end());
vector<string> ret;
for (int i = 0; i < k; ++i) {
ret.push_back(pq.top().first);
pq.pop();
}
return ret;
}
};
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online