【C++】: STL详解 —— set和map类

目录

关联式容器

键值对

set

set的概念

set的构造函数

set的使用

map

map的概念

map的构造函数

map的使用

multiset

multimap


关联式容器

C++标准库提供了多种容器,用于高效管理和操作数据集合。这些容器可分为以下几类:

  1. 顺序容器(Sequence Containers)

  2. 关联容器(Associative Containers)

  3. 容器适配器(Container Adapters)

  4. 其他类容器类型

顺序容器:核心是元素的线性存储,支持随机或顺序访问

容器分类 容器类 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::stringstd::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);                // 插入5
    s1.emplace(4);               // 原地构造插入4
   
    // ------------------------ 2. 删除与清空 ------------------------
    s1.erase(3);                 // 删除元素3
    auto 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"}

注意事项

  1. 键的唯一性 :插入重复键时,insert 会失败,operator[] 会覆盖原有值。

  2. 自定义比较器 :需满足严格弱序规则(例如不能定义 <= 作为比较逻辑)。

  3. 性能权衡 :若无需有序性,优先使用 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) 返回 01 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 处理多个值。

相关推荐
地平线开发者2 小时前
profiler debug 工具用法与高一致性策略
算法·自动驾驶
编程大师哥2 小时前
匿名函数 lambda + 高阶函数
java·python·算法
isyangli_blog2 小时前
OpenDayLight (Carbon 版本) 启动与组件安装
开发语言·php
vb2008113 小时前
FastAPI APIRouter
开发语言·python
Benszen3 小时前
KVM虚拟化解决方案
开发语言·perl
会编程的土豆3 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
東雪木3 小时前
多线程与并发编程 专属复习笔记
java·开发语言·笔记·java面试
我叫袁小陌3 小时前
算法解题思路指南
算法
MC皮蛋侠客3 小时前
C++17 多线程系列(五):C++17 并行算法——从串行到并行的零成本迁移
c++·多线程
地平线开发者3 小时前
Conv+BN+Add+ReLU 融合机制简介
算法·自动驾驶