跳到主要内容C++进阶:STL 容器 set 与 map 使用详解 | 极客日志C++算法
C++进阶:STL 容器 set 与 map 使用详解
STL 容器 set、multiset、map、multimap 及 pair 的核心特性与常用操作。涵盖容器构造、元素增删查改、迭代器使用及底层原理。通过 insert、erase、find 等函数展示用法,结合 LeetCode 题目(数组交集、环形链表、随机链表复制、前 K 个高频单词)实战演练,掌握关联式容器应用场景与性能优化。
城市逃兵17 浏览 容器
序列容器和关联容器
前面我们已经接触过 STL 中的部分容器,如:vector、list、deque等,这些容器统称为 序列式容器
序列容器的核心特征是:
- 线性逻辑结构:元素按存储位置顺序排列,相邻元素在物理存储上可能
连续(如:vector)或 离散(如:list),但元素间仅通过线性顺序关联
- 元素独立性:任意交换两个元素的位置,容器仍保持合法结构,仅改变元素的排列顺序
- 位置驱动访问:通过位置索引(如:
vector[i])或迭代器顺序访问,不依赖元素值的大小或关系
关联容器的核心特征是:
- 非线性逻辑结构:通常基于
树(如:红黑树)或 哈希表 实现,元素间通过键值的有序性或哈希映射建立关联
- 例如:二叉搜索树中左子树元素键值始终小于根节点,右子树元素键值始终大于根节点,形成紧密的逻辑关联
- 结构敏感性:交换元素会破坏容器的内部逻辑结构(如:树的有序性或哈希表的映射关系),导致后续操作(如:查找、插入)失效
- 关键字驱动访问:元素按关键字(Key)存储和检索,而非位置
- 例如:
map 容器通过键值对 (key, value) 存储数据,查找时直接通过 key 定位,时间复杂度为 O(logn)(红黑树实现)或平均 O(1)(哈希表实现)
两类容器的核心差异总结:
| 维度 | 序列式容器 | 关联式容器 |
|---|
| 逻辑结构 | 线性序列(数组、链表等) | 非线性结构(树、哈希表等) |
| 元素关联 | 仅通过 位置的顺序 关联 | 通过 键值的有序 或 哈希的映射 关联 |
| 访问依据 | 存储位置(索引或迭代器) | 关键字(Key) |
| 典型实现 | vector、list、deque | set、map
unordered_set、unordered_map |
set
一、介绍
template<class T,
class Compare = less<T>,
class Alloc = allocator<T>
class set;
关于 C++ STL 中 set 容器的模板参数说明:
- 元素类型(
T):set 底层存储的关键字类型,需保证该类型支持比较操作(默认需支持 < 运算符)
- 比较器(
Compare,默认 less<T>):用于定义元素间的排序规则。
- 内存分配器(
Allocator,默认 allocator<T>):负责管理 set 的内存分配与释放。
如需优化内存使用(如:高频插入/删除场景),可自定义分配器(如:内存池)
set<T, less<T>, MyAllocator<T>> mySet;
若 T 不支持默认比较(如:自定义类),或 需自定义排序逻辑,可通过仿函数重载比较规则
struct MyComparator {
bool operator()(const T& a, const T& b) const {
return a.custom_key < b.custom_key;
}
};
set<T, MyComparator> mySet;
*C++ 标准模板库(STL)中的 set 容器 相关知识,主要可以分为以下一个部分:*成员函数:提供了 set 容器 的 各类操作接口,涵盖元素插入、删除、查找、迭代、容量管理等功能。
1. set 容器的常见构造
#include<iostream>
#include<set>
bool fncomp(int lhs, int rhs) {
return lhs < rhs;
}
struct classcomp {
bool operator()(const int& lhs, const int& rhs) const {
return lhs < rhs;
}
};
int main() {
std::set<int> s1;
int myints[] = {10, 20, 30, 40, 50};
std::set<int> s2(myints, myints + 5);
std::set<int> s3(s2);
std::set<int> s4(s2.begin(), s2.end());
std::set<int, classcomp> s5;
bool (*fn_pt)(int, int) = fncomp;
std::set<int, bool(*)(int, int)> s6(fn_pt);
return 0;
}
2. 容量的操作
std::set::size
#include<iostream>
#include<set>
int main() {
std::set<int> myints;
std::cout << "初始状态下的 set 大小的 size 为:" << myints.size() << '\n';
for (int i = 0; i < 10; ++i) myints.insert(i);
std::cout << "插入 10 个元素后 set 大小的 size 为:" << myints.size() << '\n';
myints.insert(100);
std::cout << "插入元素 100 后 set 大小的 size 为:" << myints.size() << '\n';
myints.erase(5);
std::cout << "删除 5 个元素后 set 大小的 size 为:" << myints.size() << '\n';
return 0;
}
std::set::empty
#include<iostream>
#include<set>
int main() {
std::set<int> myset;
myset.insert(20);
myset.insert(30);
myset.insert(10);
std::cout << "myset 容器中的内容为:";
while (!myset.empty()) {
std::cout << ' ' << *myset.begin();
myset.erase(myset.begin());
}
std::cout << '\n';
return 0;
}
3. 修改的操作
std::set::clear
#include<iostream>
#include<set>
int main() {
std::set<int> myset;
myset.insert(100);
myset.insert(200);
myset.insert(300);
std::cout << "初始状态下 myset 容器中的内容为:";
for (std::set<int>::iterator it = myset.begin(); it != myset.end(); ++it) {
std::cout << ' ' << *it;
}
std::cout << '\n';
myset.clear();
myset.insert(1101);
myset.insert(2202);
std::cout << "进行清空和插入操作后 myset 容器中的内容为:";
for (std::set<int>::iterator it = myset.begin(); it != myset.end(); ++it) {
std::cout << ' ' << *it;
}
std::cout << '\n';
return 0;
}
std::set::swap
#include<iostream>
#include<set>
int main() {
int myints[] = {12, 75, 10, 32, 20, 25};
std::set<int> first(myints, myints + 3);
std::set<int> second(myints + 3, myints + 6);
first.swap(second);
std::cout << "交换后 first 容器中的内容为:";
for (std::set<int>::iterator it = first.begin(); it != first.end(); ++it) {
std::cout << ' ' << *it;
}
std::cout << '\n';
std::cout << "交换后 second 容器中的内容为:";
for (std::set<int>::iterator it = second.begin(); it != second.end(); ++it) {
std::cout << ' ' << *it;
}
std::cout << '\n';
return 0;
}
std::set::insert
#include<iostream>
#include<set>
int main() {
std::set<int> myset;
std::set<int>::iterator it;
std::pair<std::set<int>::iterator, bool> ret;
for (int i = 1; i <= 5; ++i) {
myset.insert(i * 10);
}
ret = myset.insert(20);
if (ret.second == false) it = ret.first;
std::cout << "myset 容器中的内容为:";
for (it = myset.begin(); it != myset.end(); ++it) {
std::cout << ' ' << *it;
}
std::cout << '\n';
myset.insert(it, 25);
myset.insert(it, 24);
myset.insert(it, 26);
std::cout << "myset 容器中的内容为:";
for (it = myset.begin(); it != myset.end(); ++it) {
std::cout << ' ' << *it;
}
std::cout << '\n';
int myints[] = {5, 10, 15};
myset.insert(myints, myints + 3);
std::cout << "myset 容器中的内容为:";
for (it = myset.begin(); it != myset.end(); ++it) {
std::cout << ' ' << *it;
}
std::cout << '\n';
return 0;
}
std::set::erase
#include<iostream>
#include<set>
int main() {
std::set<int> myset;
std::set<int>::iterator it;
for (int i = 1; i < 10; i++) {
myset.insert(i * 10);
}
it = myset.begin(); ++it;
myset.erase(it);
myset.erase(40);
it = myset.find(60);
myset.erase(it, myset.end());
std::cout << "myset 容器中的内容为:";
for (it = myset.begin(); it != myset.end(); ++it) {
std::cout << ' ' << *it;
}
std::cout << '\n';
return 0;
}
3. 比较的操作
std::set::key_comp
#include<iostream>
#include<set>
int main() {
std::set<int> myset;
std::set<int>::key_compare mycomp = myset.key_comp();
for (int i = 0; i <= 5; i++) {
myset.insert(i);
}
std::cout << "myset 容器的内容为:";
int highest;
highest = *myset.rbegin();
std::set<int>::iterator it = myset.begin();
do {
std::cout << ' ' << *it;
} while (mycomp(*(++it), highest));
std::cout << '\n';
return 0;
}
std::set::value_comp
#include<iostream>
#include<set>
int main() {
for (int i = 0; i <= 5; i++) {
myset.insert(i);
}
std::cout << "myset 容器中内容为:";
do {
std::cout << ' ' << *it;
} while (mycomp(*(++it), highest));
std::cout << '\n';
return 0;
}
4. 其他的操作
std::set::find
#include<iostream>
#include<set>
int main() {
std::set<int>::iterator it;
for (int i = 1; i <= 5; i++) {
myset.insert(i * 10);
}
it = myset.find(20);
myset.erase(it);
myset.erase(myset.find(40));
std::cout << "myset 容器中的内容为:";
for (it = myset.begin(); it != myset.end(); ++it) {
std::cout << ' ' << *it;
}
std::cout << '\n';
return 0;
}
std::set::count
#include<iostream>
#include<set>
int main() {
std::set<int> myset;
for (int i = 1; i < 5; ++i) {
myset.insert(i * 3);
}
for (int i = 0; i < 10; ++i) {
std::cout << i;
if (myset.count(i) != 0) std::cout << "在 myset 容器中\n";
else std::cout << "不在 myset 容器中\n";
}
return 0;
}
std::set::lower_bound
#include<iostream>
#include<set>
int main() {
std::set<int> myset;
std::set<int>::iterator itlow, itup;
for (int i = 1; i < 10; i++) myset.insert(i * 10);
itlow = myset.lower_bound(30);
itup = myset.upper_bound(60);
myset.erase(itlow, itup);
std::cout << "myset 容器的内容为:";
for (std::set<int>::iterator it = myset.begin(); it != myset.end(); ++it) {
std::cout << ' ' << *it;
}
std::cout << '\n';
return 0;
}
std::set::upper_bound
std::set::equal_range
#include<iostream>
#include<set>
int main() {
std::set<int> myset;
for (int i = 1; i <= 5; i++) myset.insert(i * 10);
std::pair<std::set<int>::const_iterator, std::set<int>::const_iterator> ret;
ret = myset.equal_range(30);
std::cout << "lower_bound(ret.first)指向:" << *ret.first << '\n';
std::cout << "upper_bound(ret.second)指向:" << *ret.second << '\n';
return 0;
}
二、使用
代码示例 1:插入 + 迭代器遍历 + 批量插入 + set 容器存储字符串类型的变量
#include<iostream>
#include<set>
using namespace std;
int main() {
set<int> s;
s.insert(5);
s.insert(2);
s.insert(7);
s.insert(5);
auto it = s.begin();
while (it != s.end()) {
cout << *it << " ";
++it;
}
cout << endl;
s.insert({2, 8, 3, 9});
for (auto e : s) {
cout << e << " ";
}
cout << endl;
set<string> strset = {"sort", "insert", "add"};
for (auto& e : strset) {
cout << e << " ";
}
cout << endl;
return 0;
}
代码示例 2:迭代器的遍历 + 删除的'迭代器位置删除、按值删除' + 先查找再删除 + 两种查找算法的对比 + 使用 count 间接判断元素是否存储在
#include<iostream>
#include<set>
using namespace std;
int main() {
set<int> s = {4, 2, 7, 2, 8, 5, 9};
for (auto& e : s) {
cout << e << " ";
}
cout << endl;
s.erase(s.begin());
for (auto e : s) {
cout << e << " ";
}
cout << endl;
int x;
cin >> x;
int num = s.erase(x);
if (num == 0) {
cout << x << " 不存在!" << endl;
} else {
cout << x << " 存在!" << endl;
}
for (auto e : s) {
cout << e << " ";
}
cout << endl;
cin >> x;
auto pos = s.find(x);
if (pos != s.end()) {
s.erase(pos);
} else {
cout << x << " 不存在!" << endl;
}
for (auto e : s) {
cout << e << " ";
}
cout << endl;
auto pos1 = find(s.begin(), s.end(), x);
auto pos2 = s.find(x);
cin >> x;
if (s.count(x))
{
cout << x << " 在!" << endl;
} else {
cout << x << " 不存在!" << endl;
}
return 0;
}
代码案例 3:插入 + 迭代器遍历 + lower_bound/upper_bound 的使用 + erase 的迭代器范围删除
#include<iostream>
#include<set>
using namespace std;
int main() {
std::set<int> myset;
for (int i = 1; i < 10; i++) {
myset.insert(i * 10);
}
cout << "初始状态下的 myset 容器中的内容为:" << endl;
for (auto& e : myset) {
cout << e << " ";
}
cout << endl;
auto itlow = myset.lower_bound(30);
auto itup = myset.upper_bound(60);
myset.erase(itlow, itup);
cout << "进行迭代器范围删除之后 myset 容器中的内容为:" << endl;
for (auto e : myset) {
cout << e << " ";
}
cout << endl;
return 0;
}
multiset
一、介绍
C++ 标准模板库(STL)中的 multiset 容器 的接口函数,主要可以分为以下一个部分:
二、区别
template<class T,
class Compare = less<T>,
class Alloc = allocator<T>>
class multiset;
可以发现,它与 set 容器的模板声明形式完全一致都通过 T(元素类型)、Compare(比较规则)、Alloc(内存分配器) 这三个模板参数定义容器。
但需要注意:set 和 multiset 虽模板声明形式相同,核心特性却有本质区别
set 会自动去重,容器中元素唯一
multiset 允许元素重复,侧重按序存储重复数据
代码示例:multiset 容器和 set 容器的区别
#include<iostream>
#include<set>
using namespace std;
int main() {
multiset<int> s = {4, 2, 7, 2, 4, 8, 4, 5, 4, 9};
cout << "初始状态的 multiset 容器中的内容为:" << endl;
auto it = s.begin();
while (it != s.end()) {
cout << *it << " ";
++it;
}
cout << endl;
cout << "请输入你想查找的节点的键" << endl;
int x;
cin >> x;
auto pos = s.find(x);
cout << "multiset 容器中所有键为" << x << "的节点为:" << endl;
while (pos != s.end() && *pos == x) {
cout << *pos << " ";
++pos;
}
cout << endl;
cout << "multiset 容器中键为" << x << "的节点的数量为:" << endl;
cout << s.count(x) << endl;
cout << "请输入你想删除的节点的键" << endl;
cin >> x;
cout << "删除所有键为" << x << "的节点之后 multiset 容器中的内容为:" << endl;
s.erase(x);
for (auto it : s) {
cout << it << " ";
}
cout << endl;
return 0;
}
pair
一、介绍
pair:用于将两个不同类型(或相同类型)的数据组合成一个逻辑单元。它是 C++ 标准库(<utility> 头文件)中的一个模板类其核心设计思想是 '存储一对相关联的值',
语法形式如下:std::pair<Type1, Type2> pairName(value1, value2);
二、特性
1. 元素访问方式通过 first 和 second 成员变量直接访问:
2. 默认构造与赋值****无参构造:std::pair<int, double> p;(first 和 second 为默认值,如:0 和 0.0)拷贝构造:std::pair p2 = p1;赋值操作:p2 = p1;
3. 比较操作支持按字典序比较(先比 first,若相等再比 second):
三、使用
typedef pair<const Key, T> value_type;
template<class T1, class T2>
struct pair {
typedef T1 first_type;
typedef T2 second_type;
T1 _first;
T2 _second;
pair() : _first(T1()), _second(T2()) {}
pair(const T1& a, const T2& b) : _first(a), _second(b) {}
template<class U, class V>
pair(const pair<U, V>& pr) : _first(pr.first), _second(pr.second) {}
};
template<class T1, class T2>
inline pair<T1, T2> make_pair(T1 x, T2 y) {
return (pair<T1, T2>(x, y));
}
map
一、介绍
template<class Key,
class T,
class Compare = less<Key>,
class Alloc = allocator<pair<const Key, T>>
class map;
关于 C++ STL 中 map 容器的模板参数说明:
- 键类型(
Key):map 中用于索引和排序的关键字类型,它决定了 map 容器的查找依据。需保证该类型支持比较操作(默认需支持 < 运算符 ),map 会根据键的大小关系,自动维护键值对的有序性(默认按键升序排列 )
- 映射值类型(
T):map 中与键关联的具体数据类型,也就是常说的'映射值'类型。键(Key)用于查找,而 T 用于存储与键对应的实际业务数据,二者共同构成 pair<const Key, T> 类型的键值对,存于 map 容器中
- 比较器(
Compare,默认 less<Key>):用于定义键之间的排序规则。
- 内存分配器(
Alloc,默认 allocator<pair<const Key, T>>):负责管理 map 底层存储的键值对(pair<const Key, T> 类型)的内存分配与释放。
简单来说:map 通过这四个模板参数,灵活控制了 键的类型、映射值的类型、键的排序规则 以及 内存管理方式,让开发者可根据实际需求定制 map 的行为,适配不同的业务场景。
如需优化内存使用(如:高频插入、删除键值对的场景,或对内存分配有特殊定制需求 ),可自定义分配器(如:实现内存池等 )
map<Key, T, less<Key>, MyAllocator<pair<const Key, T>>> myMap;
若 Key 不支持默认比较(如:自定义类作为键,未重载 < 运算符 ),或需自定义键的排序逻辑(如:按键降序、按键的某一成员排序等 ),可通过仿函数重载比较规则
struct MyKeyComparator {
bool operator()(const Key& a, const Key& b) const {
return a.custom_member < b.custom_member;
}
};
map<Key, T, MyKeyComparator> myMap;
C++ 标准模板库(STL) 中的 map 容器 的接口函数,主要可以分为以下一个部分:
1. map 容器的常见构造
#include<iostream>
#include<map>
bool fncomp(char lhs, char rhs) {
return lhs < rhs;
}
struct classcomp {
bool operator()(const char& lhs, const char& rhs) const {
return lhs < rhs;
}
};
int main() {
std::map<char, int> m1;
m1['a'] = 10; m1['b'] = 30; m1['c'] = 50; m1['d'] = 70;
std::map<char, int> m2(m1.begin(), m1.end());
std::map<char, int> m3(m2);
std::map<char, int, classcomp> m4;
bool (*fn_pt)(char, char) = fncomp;
std::map<char, int, bool(*)(char, char)> m5(fn_pt);
return 0;
}
2. 容量的操作
std::map::size
#include<iostream>
#include<map>
int main() {
return 0;
}
std::map::empty
#include<iostream>
#include<map>
int main() {
while (!mymap.empty()) {
std::cout << mymap.begin()->first << " => " << mymap.begin()->second << '\n';
mymap.erase(mymap.begin());
}
return 0;
}
3. 访问的操作
map 提供了两种修改值的方式:
- 通过迭代器:无论是
遍历过程中 还是通过 find 获取到键的迭代器后,都可以修改对应的值
- 通过 operator[]:这是一个多功能接口,不仅支持
修改已存在的键值对,还能用于 插入新数据 或 查找数据(当键不存在时会自动插入默认值)
std::map::operator[]
#include<iostream>
#include<map>
#include<string>
int main() {
std::map<char, std::string> mymap;
mymap['a'] = "an element";
mymap['b'] = "another element";
mymap['c'] = mymap['b'];
std::cout << "mymap['a'] is " << mymap['a'] << '\n';
std::cout << "mymap['b'] is " << mymap['b'] << '\n';
std::cout << "mymap['c'] is " << mymap['c'] << '\n';
std::cout << "mymap['d'] is " << mymap['d'] << '\n';
std::cout << "mymap now contains " << mymap.size() << " elements.\n";
return 0;
}
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main() {
map<string, string> dict;
dict.insert(make_pair("sort", "排序"));
dict["insert"];
dict["left"] = "左边";
dict["left"] = "左边、剩余";
cout << dict["left"] << endl;
return 0;
}
4. 修改的操作
std::map::clear
#include<iostream>
#include<map>
int main() {
return 0;
}
std::map::swap
#include<iostream>
#include<map>
int main() {
std::map<char, int> foo, bar;
foo['x'] = 100; foo['y'] = 200;
bar['a'] = 11; bar['b'] = 22; bar['c'] = 33;
foo.swap(bar);
std::cout << "foo 容器中的内容为:\n";
for (std::map<char, int>::iterator it = foo.begin(); it != foo.end(); ++it) {
std::cout << it->first << " => " << it->second << '\n';
}
std::cout << "bar 容器中的内容为:\n";
for (std::map<char, int>::iterator it = bar.begin(); it != bar.end(); ++it) {
std::cout << it->first << " => " << it->second << '\n';
}
return 0;
}
std::map::insert
#include<iostream>
#include<map>
int main() {
std::map<char, int> mymap;
mymap.insert(std::pair<char, int>('a', 100));
mymap.insert(std::pair<char, int>('z', 200));
std::pair<std::map<char, int>::iterator, bool> ret;
ret = mymap.insert(std::pair<char, int>('z', 500));
if (ret.second == false) {
std::cout << "元素'z'已经存在\n";
std::cout << "其值为:" << ret.first->second << '\n';
}
std::map<char, int>::iterator it = mymap.begin();
mymap.insert(it, std::pair<char, int>('b', 300));
mymap.insert(it, std::pair<char, int>('c', 400));
std::map<char, int> anothermap;
anothermap.insert(mymap.begin(), mymap.find('c'));
std::cout << "mymap 容器中的内容为:\n";
for (it = mymap.begin(); it != mymap.end(); ++it) {
std::cout << it->first << " => " << it->second << '\n';
}
std::cout << "anothermap 容器中内容为:\n";
for (it = anothermap.begin(); it != anothermap.end(); ++it) {
std::cout << it->first << " => " << it->second << '\n';
}
return 0;
}
std::map::erase
#include<iostream>
#include<map>
int main() {
std::map<char, int> mymap;
std::map<char, int>::iterator it;
mymap['a'] = 10; mymap['b'] = 20; mymap['c'] = 30; mymap['d'] = 40; mymap['e'] = 50; mymap['f'] = 60;
it = mymap.find('b');
mymap.erase(it);
mymap.erase('c');
it = mymap.find('e');
mymap.erase(it, mymap.end());
for (it = mymap.begin(); it != mymap.end(); ++it) {
std::cout << it->first << " => " << it->second << '\n';
}
return 0;
}
5. 其他的操作
std::map::find
#include<iostream>
#include<map>
int main() {
std::map<char, int> mymap;
std::map<char, int>::iterator it;
mymap['a'] = 50; mymap['b'] = 100; mymap['c'] = 150; mymap['d'] = 200;
it = mymap.find('b');
if (it != mymap.end()) mymap.erase(it);
std::cout << "mymap 容器中元素为:" << '\n';
std::cout << "a => " << mymap.find('a')->second << '\n';
std::cout << "c => " << mymap.find('c')->second << '\n';
std::cout << "d => " << mymap.find('d')->second << '\n';
return 0;
}
std::map::count
#include<iostream>
#include<map>
int main() {
std::map<char, int> mymap;
char c;
mymap['a'] = 101; mymap['c'] = 202; mymap['f'] = 303;
for (c = 'a'; c < 'h'; c++) {
std::cout << c;
if (mymap.count(c) > 0)
std::cout << "是 mymap 容器中的元素\n";
else
std::cout << "不是 mymap 容器中的元素\n";
}
return 0;
}
std::map::lower_bound
#include<iostream>
#include<map>
int main() {
std::map<char, int> mymap;
std::map<char, int>::iterator itlow, itup;
mymap['a'] = 20; mymap['b'] = 40; mymap['c'] = 60; mymap['d'] = 80; mymap['e'] = 100;
itlow = mymap.lower_bound('b');
itup = mymap.upper_bound('d');
mymap.erase(itlow, itup);
for (std::map<char, int>::iterator it = mymap.begin(); it != mymap.end(); ++it) {
std::cout << it->first << " => " << it->second << '\n';
}
return 0;
}
std::map::upper_bound
std::map::equal_range
#include<iostream>
#include<map>
int main() {
std::map<char, int> mymap;
mymap['a'] = 10;
mymap['b'] = 20;
mymap['c'] = 30;
std::pair<std::map<char, int>::iterator, std::map<char, int>::iterator> ret;
ret = mymap.equal_range('b');
std::cout << "lower_bound 指向的元素是:";
std::cout << ret.first->first << " => " << ret.first->second << '\n';
std::cout << "upper_bound 指向的元素是:";
std::cout << ret.second->first << " => " << ret.second->second << '\n';
return 0;
}
二、总结:
代码示例:map 核心的接口函数的设计说明 (find/insert/operator[])
#include<map>
using namespace std;
using key_type = ;
using mapped_type = ;
using value_type = pair<const key_type, mapped_type>;
iterator find(const key_type& k);
pair<iterator, bool> insert(const value_type& val);
mapped_type& operator[](const key_type& k) {
pair<iterator, bool> ret = insert({k, mapped_type()});
iterator it = ret.first;
return it->second;
}
三、使用
代码示例 1:使用'构造函数 + insert() 函数 + find() 函数'
#include<iostream>
#include<map>
using namespace std;
int main() {
map<string, string> dict = {{"left", "左边"}, {"right", "右边"}, {"insert", "插入"}, {"string", "字符串"}};
auto it = dict.begin();
while (it != dict.end()) {
cout << it->first << ":" << it->second << endl;
++it;
}
cout << endl;
pair<string, string> kv1("first", "第一个");
dict.insert(kv1);
dict.insert(pair<string, string>("second", "第二个"));
dict.insert(make_pair("sort", "排序"));
dict.insert({"auto", "自动的"});
dict.insert({"left", "左边,剩余"});
for (const auto& e : dict) {
cout << e.first << ":" << e.second << endl;
}
cout << endl;
string str;
while (cin >> str) {
auto ret = dict.find(str);
if (ret != dict.end()) {
cout << "=> " << ret->second << endl;
} else {
cout << "无此单词,请重新输入" << endl;
}
}
return 0;
}
代码片段 2:用'find + 迭代器'统计水果次数
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main() {
string arr[] = {"苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉"};
map<string, int> countMap;
for (const auto& str : arr) {
auto ret = countMap.find(str);
if (ret == countMap.end()) {
countMap.insert({str, 1});
} else {
ret->second++;
}
}
for (const auto& e : countMap) {
cout << e.first << ":" << e.second << endl;
}
cout << endl;
return 0;
}
代码片段 3:用 operator[] 简化统计逻辑
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main() {
string arr[] = {"苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉"};
map<string, int> countMap;
for (const auto& str : arr) {
countMap[str]++;
}
for (const auto& e : countMap) {
cout << e.first << ":" << e.second << endl;
}
cout << endl;
return 0;
}
multimap
一、介绍
C++ 标准模板库(STL)中的 multimap 容器 的接口函数,主要可以分为以下一个部分:
二、区别
template<class Key,
class T,
class Compare = less<Key>,
class Alloc = allocator<pair<const Key, T>>
class multimap;
可以发现,它与 map 容器的模板声明形式完全一致都通过 Key(键类型)、T(映射值类型)、Compare(比较规则)、Alloc(内存分配器) 这四个模板参数定义容器。
但需要注意:map 和 multimap 虽模板声明形式相似,核心特性却有本质区别
map 会自动保证键唯一,容器中每个键对应唯一的映射关系
multimap 允许键冗余,侧重按序存储键可重复的映射数据
*简单说:*map 像'严格的字典',同一个字(键)只能查一个解释(值),重复插入同键会静默 覆盖/失败。multimap 像'宽松的便签本',同一个字(键)能记多个解释(值),适合存'一对多'的映射关系。
map 和 multimap 的基础用法高度相似,核心区别在于 multimap 支持键(key)的冗余存储
基于这一特性,它的 insert(插入)、find(查找)、count(计数)、erase(删除)等操作,都会围绕'键可重复'进行适配这一点和 set/multiset 的差异逻辑完全一致(比如:find 遇到重复键时,会返回中序遍历的第一个匹配元素)
此外,multimap 不支持 operator[]
因为键可冗余的场景下,[] 无法明确指定修改哪一个键对应的值
它仅能用于插入新键值对(但也会因'键重复'的特性,失去 map 中 [] 兼具'查找 + 修改'的复合能力)
OJ 练习
一、set
题目介绍
方法一:
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;
}
};
题目介绍
方法一:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
set<ListNode*> s;
ListNode* cur = head;
while (cur)
{
auto ret = s.insert(cur);
if (ret.second == false) return cur;
cur = cur->next;
}
return nullptr;
}
};
二、map
题目介绍
方法一:
class Solution {
public:
Node* copyRandomList(Node* head)
{
map<Node*, Node*> m;
Node* copyHead = nullptr;
Node* copyTail = nullptr;
Node* curr = head;
while (curr)
{
if (copyTail == nullptr) {
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)
{
if (curr->random == nullptr) {
copy->random = nullptr;
}
else {
copy->random = m[curr->random];
}
curr = curr->next;
copy = copy->next;
}
return copyHead;
}
};
题目介绍
方法一:基于 stable_sort 排序
class Solution {
public:
struct Compare {
bool operator()(const pair<string, int>& x, const pair<string, int>& y) const {
return x.second > y.second;
}
};
vector<string> topKFrequent(vector<string>& words, int k) {
map<string, int> m;
vector<string> ret;
for (auto& e : words) {
m[e]++;
}
vector<pair<string, int>> v(m.begin(), m.end());
stable_sort(v.begin(), v.end(), Compare());
for (int i = 0; i < k; i++) {
ret.push_back(v[i].first);
}
return ret;
}
};
方法二:基于 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;
vector<string> ret;
for (auto& e : words) {
m[e]++;
}
vector<pair<string, int>> v(m.begin(), m.end());
sort(v.begin(), v.end(), Compare());
for (int i = 0; i < k; i++) {
ret.push_back(v[i].first);
}
return ret;
}
};
方法三:基于 priority_queue 大顶堆
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;
vector<string> ret;
for (auto& e : words) {
m[e]++;
}
priority_queue<pair<string, int>, vector<pair<string, int>>, Compare> p(m.begin(), m.end());
for (int i = 0; i < k; i++) {
ret.push_back(p.top().first);
p.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