map和set的使用

1. set系列的使用

1.1 set和multiset参考文档

set和multiset参考文档


1.2 set类的介绍

set 是一个关联容器,用于存储唯一且有序的元素。其模板定义如下:

cpp 复制代码
template <class Key, class Compare = less<Key>, class Allocator = allocator<Key>>
class set;
  • Key:表示集合中元素的类型,即关键字类型。
  • Compare :比较函数对象,默认为 less<Key>,要求支持 < 操作符或可调用的仿函数,用于确定元素之间的顺序。
  • Allocator:内存分配器,默认使用标准分配器。

⚠️ 关键点:

  • set 要求元素类型 Key 必须支持"小于"比较(即 a < b 成立),否则无法构建红黑树结构。
  • 若需自定义排序规则(如降序、字符串忽略大小写等),可通过自定义仿函数传入 Compare 参数。
  • 底层实现为 红黑树(Red-Black Tree) ,保证插入、删除、查找的时间复杂度均为 O(log N)。
  • 所有元素在内部按升序排列,迭代器遍历遵循中序遍历顺序。
  • 不允许重复元素,每个键值唯一。
cpp 复制代码
// 示例:自定义比较方式(降序)
struct Greater {
    bool operator()(const int& a, const int& b) const {
        return a > b; // 大的在前
    }
};

set<int, Greater> s; // 降序排列的 set

1.3 set的构造与迭代器

set 支持多种构造方式:

cpp 复制代码
set<int> s1;                    // 空 set
set<int> s2 = {1, 5, 3, 2};     // 构造并初始化
set<int> s3(s2.begin(), s2.end()); // 迭代器范围构造
set<int> s4(s2);                // 拷贝构造

迭代器类型:

  • iterator:可读可写(但不可修改键值)
  • const_iterator:只读

❗ 重要限制:

  • 不能通过迭代器修改 set 中的元素值(即 key)。
  • 因为修改键值会破坏红黑树的有序性,导致未定义行为。
cpp 复制代码
set<int> s = {10, 20, 30};

for (auto it = s.begin(); it != s.end(); ++it) {
    // *it = 100; // 错误!不允许修改 key
}

✅ 支持范围 for 循环:

cpp 复制代码
for (const auto& x : s) {
    cout << x << " "; // 输出:10 20 30
}

反向遍历:

cpp 复制代码
for (auto rit = s.rbegin(); rit != s.rend(); ++rit) {
    cout << *rit << " "; // 输出:30 20 10
}

1.4 set的增删查操作
插入操作
cpp 复制代码
set<int> s;

// insert(value) ------ 只插入,返回一个 pair<iterator, bool>
auto ret = s.insert(5);
if (ret.second) {
    cout << "插入成功" << endl;
} else {
    cout << "已存在,插入失败" << endl;
}

// insert(first, last) ------ 插入范围
vector<int> v = {1, 2, 3};
s.insert(v.begin(), v.end());

// emplace() ------ 直接构造元素,效率更高
s.emplace(100);

✅ insert() 返回值是一个 pair<iterator, bool>:

  • first:指向插入位置的迭代器
  • second:true 表示插入成功,false 表示已有该元素
查找操作
cpp 复制代码
// find(key) ------ 找到返回迭代器,未找到返回 end()
auto it = s.find(5);
if (it != s.end()) {
    cout << "找到: " << *it << endl;
} else {
    cout << "未找到" << endl;
}

// count(key) ------ 统计出现次数(set中永远是 0 或 1)
int cnt = s.count(5); // 0 或 1

🔍 注意:count() 在 set 中无实际意义(总是 0 或 1),但在 multiset 中非常有用。

删除操作
cpp 复制代码
// erase(iterator)
auto it = s.find(5);
if (it != s.end()) {
    s.erase(it);
}

// erase(key) ------ 删除所有等于 key 的元素(但 set 中只有一个)
s.erase(5);

// erase(first, last) ------ 删除区间
s.erase(s.begin(), s.end());

📌 erase(key) 会删除所有匹配的元素,但由于 set 不允许重复,最多只删一个。


1.5 multiset 和 set 的差异

multiset 与 set 基本结构一致,区别在于:

  • 允许重复元素(多重集)
  • 底层仍为红黑树,保持有序
  • 插入、删除、查找时间复杂度仍为 O(log N)
cpp 复制代码
multiset<int> ms = {1, 1, 2, 3, 3, 3};

// count() 返回重复数量
cout << ms.count(3) << endl; // 输出:3

// find() 只返回第一个匹配的迭代器
auto it = ms.find(3);
if (it != ms.end()) {
    cout << *it << endl; // 输出:3
}

// erase(key) 会删除所有相同键值
ms.erase(3); // 移除全部三个 3

⚠️ multiset 不支持 operator[],因为多个键对应多个值,无法确定唯一映射。


2. map系列的使用

2.1 map和multimap参考文档

map和multimap参考文档


2.2 map类的介绍

map 是一个关联容器,存储键值对(key-value),其中每个键唯一。

模板定义:

cpp 复制代码
template <class Key, class T, class Compare = less<Key>, class Allocator = allocator<pair<const Key, T>>>
class map;
  • Key :键类型,必须支持 < 比较
  • T:值类型(mapped_type)
  • Compare:比较函数对象
  • Allocator:内存分配器

✅ 特性:

  • 底层实现为 红黑树
  • 按照 Key 升序排列
  • 支持快速插入、删除、查找,时间复杂度均为 O(log N)
  • map 中的键是唯一的,不允许重复
cpp 复制代码
map<string, int> scores = {
    {"Alice", 95},
    {"Bob", 87},
    {"Charlie", 92}
};

2.3 pair类型介绍

map 内部节点以 pair<const Key, T> 形式存储键值对。

cpp 复制代码
typedef pair<const Key, T> value_type;

🔑 const Key 表明:键一旦创建就不能被修改,否则破坏红黑树结构。

cpp 复制代码
auto p = make_pair("x", 100);
// p.first = "y"; // 编译错误!first 是 const

2.4 map的构造方式
cpp 复制代码
map<string, int> m1;                           // 空 map
map<string, int> m2 = {{"A", 1}, {"B", 2}};    // 初始化列表
map<string, int> m3(m2.begin(), m2.end());     // 迭代器范围构造
map<string, int> m4(m2);                       // 拷贝构造

支持正向与反向迭代:

cpp 复制代码
for (auto it = m4.begin(); it != m4.end(); ++it) {
    cout << it->first << ": " << it->second << endl;
}

// 反向遍历
for (auto rit = m4.rbegin(); rit != m4.rend(); ++rit) {
    cout << rit->first << ": " << rit->second << endl;
}

✅ 支持范围 for:

cpp 复制代码
for (const auto& [k, v] : m4) {
    cout << k << " -> " << v << endl;
}

2.5 map的增删查操作
插入操作
cpp 复制代码
map<string, int> m;

// insert(pair) ------ 插入键值对
m.insert({"Alice", 95});

// insert({key, value}) ------ C++11 风格
m.insert(make_pair("Bob", 87));

// emplace() ------ 更高效,直接构造
m.emplace("Charlie", 92);

// insert(iterator, value) ------ 位置提示优化性能
auto hint = m.begin();
m.insert(hint, {"David", 90});

✅ insert() 返回 pair<iterator, bool>:

  • second == true:插入成功
  • second == false:键已存在,插入失败
查找操作
cpp 复制代码
// find(key) ------ 找到返回迭代器,未找到返回 end()
auto it = m.find("Alice");
if (it != m.end()) {
    cout << "分数: " << it->second << endl;
}

// count(key) ------ 0 或 1(因为键唯一)
int cnt = m.count("Alice"); // 1
删除操作
cpp 复制代码
// erase(iterator)
m.erase(m.find("Alice"));

// erase(key) ------ 删除指定键的所有条目(仅一个)
m.erase("Bob");

// erase(first, last) ------ 删除区间
m.erase(m.begin(), m.end());

2.6 map的数据修改机制

map 支持修改 value,但禁止修改 key。

1. 通过迭代器修改值
cpp 复制代码
auto it = m.find("Charlie");
if (it != m.end()) {
    it->second = 95; // ✅ 可以修改 value
}
2. 通过 operator[] ------ 强大的复合接口
cpp 复制代码
m["Alice"] = 98; // ✅ 存在则更新,不存在则插入

💡 operator[] 的行为:

  • 如果键存在,返回对应的 value 引用,可修改;
  • 如果键不存在,自动插入一个默认值(如 0 或空构造),然后返回引用。
cpp 复制代码
m["NewUser"] = 100; // 键不存在 → 自动插入 ("NewUser", 0),再赋值为 100

⚠️ 使用注意:

  • operator[] 会隐式插入新键,可能造成意外数据膨胀。
  • 若只想查询而不插入,应使用 find()。

2.7 multimap 和 map 的差异

multimap 与 map 结构相似,主要区别如下:

特性 map multimap
键是否唯一 是 否(允许多个相同键)
operator[] 支持 不支持(因无法确定唯一值)
insert() 插入单个键值对 可插入多个相同键
find() 找到第一个匹配项 找到第一个匹配项
count() 总是 0 或 1 返回相同键的数量
erase(key) 删掉所有同键元素(仅一个) 删掉所有同键元素
cpp 复制代码
multimap<string, int> mm = {
    {"Math", 90},
    {"Math", 85},
    {"English", 92}
};

// find 只返回第一个匹配的
auto it = mm.find("Math");
if (it != mm.end()) {
    cout << "第一门数学成绩: " << it->second << endl; // 90
}

// count 统计数量
cout << mm.count("Math") << endl; // 2

// erase(key) 删掉所有相同键
mm.erase("Math"); // 两个都删了

❌ mm["Math"] = 95; ------ 编译错误!multimap 不支持 operator[]


总结对比表

容器 是否允许重复键 是否支持 operator[] 是否支持修改 key 底层结构 排序依据
set 否 否 否 红黑树 Key 升序
multiset 是 否 否 红黑树 Key 升序
map 否 是 否 红黑树 Key 升序
multimap 是 否 否 红黑树 Key 升序

实用建议

  • 使用 set / map 时,确保 Key 类型支持 < 比较。
  • 若需自定义排序,提供仿函数作为 Compare 模板参数。
  • 优先使用 find() 查询,避免 operator[] 导致不必要的插入。
  • 对于需要重复键的场景,选择 multiset / multimap。
  • 迭代时避免修改 key,否则程序崩溃或行为异常。
  • emplace() 比 insert() 效率更高,尤其适用于复杂对象。
相关推荐
余额瞒着我当琳1 小时前
C++异常,异常的概念,异常抛出与捕获,RAII 和智能指针的设计思路,C++标准库一些指针的使用
java·开发语言·c++·算法
dadaobusi1 小时前
error:field ifru_addr has incomplete type sockaddr
学习
2601_970703831 小时前
除湿机原理研究报告:一台机器在物理上能走多远
大数据·数据结构·算法·数据库架构·推荐算法
骑着蜗牛撵大象3271 小时前
大模型 SDK 编译就崩?用 CMakeLists 把 httplib、JSON 与 OpenSSL 装配成可复用的 ChatServer
android·java·c++·http·json·cmake·大模型sdk
若亦_1 小时前
排序>>选择排序
数据结构·算法·排序算法
重生之小比特1 小时前
【算法拾光】前缀和|一维前缀和 + 二维前缀和
数据结构·算法
Z21-X2 小时前
云原生运维笔记:硬核拆解OpenStack计算与存储——从主机聚合到Cinder挂载全流程
运维·笔记·学习·云原生·openstack
坤坤子吖2 小时前
Linux网络编程基础:从 MAC 到 IP,理解网络数据是怎么传输的
linux·网络·笔记·学习
霅昪忞昉2 小时前
DSP_28002x的SPI代码注释_文3
笔记·dsp开发