目录
关联式容器
键值对
set
set的概念
set的构造函数
set的使用
map
map的概念
map的构造函数
map的使用
multiset
multimap
关联式容器
C++标准库提供了多种容器,用于高效管理和操作数据集合。这些容器可分为以下几类:
-
顺序容器(Sequence Containers)
-
关联容器(Associative Containers)
-
容器适配器(Container Adapters)
-
其他类容器类型
顺序容器:核心是元素的线性存储,支持随机或顺序访问
容器分类 | 容器类 | C++标准 | 描述 |
---|---|---|---|
顺序容器 | vector | C++98 | 动态数组,支持快速随机访问,尾部操作高效。 |
list | C++98 | 双向链表,任意位置插入/删除高效,不支持随机访问。 | |
deque | C++98 | 双端队列,头尾插入/删除高效,支持随机访问。 | |
array | C++11 | 固定大小数组,安全性优于内置数组,编译时确定大小。 | |
forward_list | C++11 | 单向链表,节省内存,仅支持单向遍历。 |
关联容器:基于键(Key)组织数据,有序容器(红黑树)与无序容器(哈希表)区分明显。
有序关联容器 | set | C++98 | 唯一键集合,基于红黑树,自动排序。 |
map | C++98 | 键值对集合(键唯一),基于红黑树,按键排序。 | |
multiset | C++98 | 允许重复键的集合,基于红黑树。 | |
multimap | C++98 | 允许重复键的键值对集合,基于红黑树。 | |
无序关联容器 | unordered_set | C++11 | 唯一键哈希集合,基于哈希表,元素无序。 |
unordered_map | C++11 | 键值对哈希表(键唯一),基于哈希表。 | |
unordered_multiset | C++11 | 允许重复键的哈希集合。 | |
unordered_multimap | C++11 | 允许重复键的键值对哈希表。 |
容器适配器:通过限制接口实现特定数据结构(如栈、队列、堆),底层依赖其他容器。
适配器 | stack | C++98 | 后进先出(LIFO)结构,默认基于std::deque 实现。 |
queue | C++98 | 先进先出(FIFO)结构,默认基于std::deque 实现。 | |
priority_queue | C++98 | 优先级队列(最大堆),默认基于std::vector 实现。 |
其他类容器类型:如std::string
和std::bitset
,虽不是严格意义上的通用容器,但提供类似容器的操作。
其他类容器类型 | string | C++98 | 字符串类,支持类似vector 的操作。 |
bitset | C++98 | 固定大小的位集合,用于位操作。 | |
valarray | C++98 | 数值计算专用数组,支持向量化操作。 | |
span | C++20 | 非拥有视图,提供对连续内存的轻量访问(如数组、vector 等)。 |
键值对
键值对(Key-Value Pair) 是一种核心数据结构,用于将唯一的键(Key) 与对应的值(Value) 关联,常用于快速查找、映射或配置管理。
-
键(Key):唯一标识符,用于快速定位值(不可重复,除非使用
multimap
)。 -
值(Value):与键关联的数据,可以是任意类型(基本类型、对象、容器等)。
键值对类型 std::pair
-
定义:
std::pair<KeyType, ValueType>
是标准库中表示键值对的模板类。 -
访问成员:通过
first
(键)和second
(值)访问。
pair<string, int> p("hello", 1);
cout << p.first << p.second << endl;
// hello1
set
set的概念
set
是一个有序关联容器,存储唯一键(Key),键本身即为其值(没有额外的Value)。
元素按键的严格弱序规则(默认升序)自动排序,不允许重复键。
特性 | 说明 |
---|---|
唯一性 | 所有元素唯一,插入重复值会被忽略(通过返回值可判断是否插入成功)。 |
自动排序 | 元素按键值自动排序(默认升序,可自定义排序规则)。 |
不可修改键 | 元素(键)在容器中不可直接修改,需先删除旧值再插入新值。 |
高效查找 | 支持 O(log n) 复杂度的查找(find() 、count() 等操作)。 |
稳定迭代器 | 插入或删除操作不会使其他元素的迭代器失效(除非指向被删除元素)。 |
- set在底层是用平衡搜索树(红黑树)实现的,所以在set当中查找某个元素的时间复杂度为 logN
- set中的元素不能被修改,因为set在底层是用平衡搜索树(红黑树)来实现的,若是对平衡搜索树(红黑树)当中某个结点的值进行了修改,那么这棵树将不再是平衡搜索树(红黑树)
set的构造函数
1、默认构造函数:创建一个空的集合,可以指定自定义的比较器(Comparator)和分配器(Allocator)。
explicit set(const Compare& comp = Compare(), const Allocator& alloc = Allocator());set<int> s1; // 默认升序排序的空集合// 自定义降序排序的比较器
struct CompareDesc
{bool operator()(int a, int b) const { return a > b; }
};
set<int, CompareDesc> s2; // 降序排列的空集合
2、范围构造函数:通过迭代器范围 [first, last)
初始化集合,元素会被自动去重并排序。
template <class InputIterator>
set(InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& alloc = Allocator());vector<int> vec = {5, 2, 2, 3, 5};
// 从 vector 的迭代器范围构造,自动去重并升序排序
std::set<int> s(vec.begin(), vec.end()); // s = {2, 3, 5}
3、拷贝构造函数:创建一个新集合,复制另一个集合的所有元素。
set(const set& other);set<int> s_original = {1, 2, 3};
set<int> s_copy(s_original); // 深拷贝,s_copy = {1, 2, 3}
4、初始化列表构造函数(C++11 起):通过初始化列表(Initializer List)直接初始化集合。
set(std::initializer_list<value_type> init, const Compare& comp = Compare(), const Allocator& alloc = Allocator());set<int> s = {3, 1, 2, 2}; // 自动去重并排序为 {1, 2, 3}
set的使用
成员函数 | 功能 |
---|---|
insert | 插入指定元素 |
erase | 删除指定元素 |
find | 查找指定元素 |
size | 获取容器中元素的个数 |
empty | 判断容器是否为空 |
clear | 清空容器 |
swap | 交换两个容器中的数据 |
count | 获取容器中指定元素值的元素个数 |
示例:
#include <iostream>
#include <set>
#include <vector>
#include <algorithm> // 集合运算所需头文件
using namespace std;// 自定义比较器(按字符串长度排序)
struct LengthCompare
{bool operator()(const string& a, const string& b) const {return a.size() < b.size();}
};int main()
{// ------------------------ 1. 初始化与插入 ------------------------set<int> s1 = {3, 1, 2, 2}; // 去重排序: {1, 2, 3}s1.insert(5); // 插入5s1.emplace(4); // 原地构造插入4// ------------------------ 2. 删除与清空 ------------------------s1.erase(3); // 删除元素3auto it = s1.find(1);if (it != s1.end()) s1.erase(it); // 通过迭代器删除// s1.clear(); // 清空集合// ------------------------ 3. 遍历与查找 ------------------------cout << "s1: ";for (int val : s1) { // C++11 范围for遍历cout << val << " "; // 输出: 2 4 5}cout << endl;// 查找示例if (s1.count(4)) {cout << "4 exists in s1" << endl;}// ------------------------ 4. 自定义排序 ------------------------set<string, LengthCompare> s3 = {"apple", "banana", "cat"};// 按长度排序: "cat", "apple", "banana"cout << "s3: ";for (const auto& str : s3) {cout << str << " ";}cout << endl;return 0;
}
map
map的概念
-
定义:
map
是一个有序关联容器,存储键值对(Key-Value Pair),其中键(Key)唯一,元素按键的严格弱序规则(默认升序)自动排序。
每个元素是一个std::pair<const Key, Value>
,键不可修改,值可以修改。 -
底层实现:
基于红黑树(Red-Black Tree,自平衡二叉搜索树),保证插入、删除和查找的时间复杂度为O(log n)
。
特性 | 说明 |
---|---|
键唯一性 | 所有键唯一,插入重复键会被忽略(可通过返回值判断是否成功)。 |
自动排序 | 元素按键自动排序(默认升序,可自定义排序规则)。 |
不可修改键 | 键在容器中不可直接修改,需先删除旧键值对再插入新键。 |
高效查找 | 支持 O(log n) 复杂度的查找(find() 、count() 等操作)。 |
稳定迭代器 | 插入或删除操作不会使其他元素的迭代器失效(除非指向被删除元素)。 |
map的构造函数
1、默认构造函数:创建一个空的 map
,可指定自定义的比较器(Comparator)和分配器(Allocator)。
explicit map(const Compare& comp = Compare(), const Allocator& alloc = Allocator());map<int, string> m1; // 空map,默认按键升序排序// 自定义按字符串长度排序的比较器
struct KeyCompare {bool operator()(const string& a, const string& b) const {return a.size() < b.size();}
};
map<string, int, KeyCompare> m2; // 键按长度排序的空map
2、范围构造函数:通过迭代器范围 [first, last)
初始化 map
,键值对会被自动去重并排序。
template <class InputIterator>
map(InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& alloc = Allocator());vector<pair<int, string>> vec = {{3, "Alice"}, {1, "Bob"}, {3, "Charlie"}};// 从vector的迭代器构造,自动去重并按键升序排序
map<int, string> m3(vec.begin(), vec.end()); // {1: "Bob", 3: "Alice"}
3、拷贝构造函数:深拷贝另一个 map
的所有键值对。
map(const map& other);map<int, string> m_original = {{1, "A"}, {2, "B"}};
map<int, string> m_copy(m_original); // 深拷贝,m_copy = {1: "A", 2: "B"}
4、初始化列表构造函数(C++11 起):通过初始化列表直接初始化 map
。
map(initializer_list<value_type> init, const Compare& comp = Compare(), const Allocator& alloc = Allocator());map<int, string> m5 = {{3, "Alice"}, {1, "Bob"}, {3, "Charlie"}}; // 去重后 {1: "Bob", 3: "Alice"}// 自定义降序排序
map<int, string, greater<int>> m6 = {{3, "A"}, {1, "B"}}; // {3: "A", 1: "B"}
注意事项
-
键的唯一性:插入重复键时,
insert
会失败,operator[]
会覆盖原有值。 -
自定义比较器:需满足严格弱序规则(例如不能定义
<=
作为比较逻辑)。 -
性能权衡:若无需有序性,优先使用
unordered_map
(哈希表实现,平均 O(1) 操作)。
map的使用
接口分类 | 接口名称 | 作用 |
---|---|---|
插入操作 | insert | 插入键值对,返回插入结果(迭代器 + 是否成功)。 |
emplace | 原地构造键值对,避免临时对象拷贝。 | |
operator[] | 通过键访问值(若键不存在,插入默认值并返回引用)。 | |
删除操作 | erase | 删除指定键或迭代器范围内的键值对。 |
clear | 清空所有键值对。 | |
查找与访问 | find | 查找键,返回迭代器(未找到返回 end() )。 |
count | 返回键的数量(0 或 1)。 | |
contains (C++20) | 检查键是否存在,返回布尔值。 | |
at | 安全访问值(键不存在时抛出异常)。 | |
容量查询 | empty | 检查容器是否为空。 |
size | 返回键值对数量。 | |
迭代器 | begin / end | 获取正向迭代器(按键升序)。 |
rbegin / rend | 获取反向迭代器(按键降序)。 |
示例:
#include <iostream>
#include <map>
#include <string>
using namespace std;int main()
{map<int, string> m;// 插入操作m.insert({1, "Alice"});m.emplace(2, "Bob"); // 原地构造m[3] = "Charlie"; // operator[] 插入// 删除操作m.erase(1); // 删除键1// m.clear(); // 清空所有元素// 查找与访问auto it = m.find(3);if (it != m.end()) {cout << "键3的值: " << it->second << endl;}// operator[] 的副作用(自动插入默认值)cout << "m[4]: " << m[4] << endl; // 输出空字符串(自动插入{4, ""})// 遍历(C++17 结构化绑定)cout << "所有键值对:" << endl;for (const auto& [key, value] : m) {cout << key << ": " << value << endl;}// 容量查询cout << "元素数量: " << m.size() << endl;cout << "是否为空: " << (m.empty() ? "是" : "否") << endl;return 0;
}
multiset
multiset容器与set容器的底层实现一样,都是平衡搜索树(红黑树),multiset容器和set容器的唯一区别就是,multiset允许键值冗余,即multiset容器当中存储的元素是可以重复的。
特性 | std::set | std::multiset |
---|---|---|
键的唯一性 | 键唯一,不允许重复 | 允许重复键 |
插入操作 | 插入重复键时失败(返回 pair<iterator, bool> ) | 总是插入成功(返回 iterator ) |
查找与计数 | count(key) 返回 0 或 1 | count(key) 可返回 >=0 的任意值 |
底层实现 | 红黑树(自平衡二叉搜索树) | 红黑树(自平衡二叉搜索树) |
时间复杂度 | 插入、删除、查找均为 O(log n) | 同 set |
典型应用场景 | 需要唯一键的有序集合(如用户ID集合) | 允许重复的有序集合(如统计成绩分布) |
-
选择
set
:需要保证键唯一性且需要有序遍历的场景(如字典、配置表)。 -
选择
multiset
:允许重复键且需要统计频率或保留重复数据的场景(如日志时间戳记录、投票统计)。
multimap
multimap容器与map容器的底层实现一样,也都是平衡搜索树(红黑树),multimap容器和map容器的区别,multimap允许键值冗余,即multimap容器当中存储的元素是可以重复的
特性 | std::map | std::multimap |
---|---|---|
键的唯一性 | 键唯一,不允许重复 | 允许重复键 |
插入操作 | 插入重复键时覆盖原有值(operator[] )或失败(insert ) | 总是插入成功,允许多个相同键的键值对共存 |
查找与访问 | operator[] 直接通过键访问值(键不存在时插入) | 没有 operator[] ,必须通过迭代器访问 |
查找结果 | find(key) 返回单个迭代器 | find(key) 返回第一个匹配键的迭代器 |
键值对数量 | 每个键对应唯一值 | 一个键可对应多个值 |
典型应用场景 | 字典、配置表(键唯一) | 一对多映射(如学生ID对应多门课程成绩) |
-
选择
map
:需要键唯一且直接通过键访问值(如用户ID到用户名的映射)。 -
选择
multimap
:允许键重复且需处理一对多关系(如订单ID对应多个商品)。 -
关键区别:
-
map
的键唯一,支持operator[]
; -
multimap
允许键重复,需用迭代器或equal_range
处理多个值。
-