C++ STL 中的 set 和 map从入门到实战指南

 更新时间:2026年10月31日 10:55:25   作者:破忒头456  
本文系统讲解C++ STL中关联式容器set和map的核心特性与使用方法,涵盖底层红黑树实现,本文结合实例代码给大家介绍C++ STL中的 set 和map完全指南从入门到实战,感兴趣的朋友一起看看吧

一、 序列式容器 vs 关联式容器

    我们之前学过 string、vector、list、deque 等,它们统称为序列式容器,因为元素在逻辑上是线性排列的,位置之间没有强关联,交换两个元素不影响“序列”性质。

    而关联式容器(如 set、map、unordered_set 等)逻辑上通常是非线性的(树或哈希),元素之间通过关键字(key)来建立关联。交换两个元素可能会破坏结构,因为它们的位置是由关键字决定的。

(1)set系列:纯 key 搜索场景,比如判断一个值在不在集合中。

(2)map系列:key/value 搜索场景,比如字典(单词→释义)。

    本章讲的 map/set 底层是红黑树(平衡二叉搜索树),因此增删查效率为 O(log N),且迭代器遍历是中序,得到的是有序序列。

二、 set 系列详解

2.1 set 的声明与特点

template < class T, 
           class Compare = less<T>, 
           class Alloc = allocator<T> > 
class set;
  • T 是关键字的类型。
  • 默认要求 T 支持 < 比较,也可以自己传仿函数。
  • 底层红黑树,去重 + 升序(默认)。
  • 迭代器(包括 const_iterator)不允许修改 key,否则会破坏树结构。

2.2 构造与迭代器

set<int> s1;                          // 空
set<int> s2 = {5,1,5,2,6};            // initializer_list,自动去重
set<int> s3(s2.begin(), s2.end());    // 区间构造
set<int> s4(s3);                      // 拷贝构造

迭代器:

  • begin()/end() 正向
  • rbegin()/rend() 反向
  • 遍历得到的是升序序列(因为中序遍历)

2.3 增删查核心接口

操作接口说明
插入insert(val)返回 pair<iterator,bool>,bool 表示是否成功(已存在则失败)
插入列表insert({...})批量插入,已存在忽略
查找find(key)返回迭代器,找不到返回 end(),O(log N)
计数count(key)返回 key 的个数(set 中只能是 0 或 1)
删除erase(key)删除等于 key 的元素,返回删除个数
删除erase(pos)删除迭代器位置
区间删除erase(first,last)删除 [first, last)
下界lower_bound(key)返回第一个 >= key 的迭代器
上界upper_bound(key)返回第一个 > key 的迭代器

2.4 综合示例:set 的基本操作

2.5 multiset —— 允许重复 key

multiset与set的唯一区别是不去重,其他接口基本一致,但有一些差异:

(1)insert总是成功。

(2)find(key)返回中序第一个等于 key 的迭代器。

(3)count(key)返回实际个数(可能 >1)。

(4)erase(key)会删除所有等于 key 的元素。

示例:

三、map 系列详解

3.1 pair 类型——key/value 的载体

map底层节点存储的是 pair<const key,T>,定义如下:

template <class T1, class T2>
struct pair {
    T1 first;   // key
    T2 second;  // value
    // 构造函数...
};

我们可以用make_pair或{}来创建 pair。

3.2 map 的声明与特点

template < class Key, 
           class T, 
           class Compare = less<Key>, 
           class Alloc = allocator<pair<const Key,T>> > 
class map;
  • Key 唯一(去重),按 Key 升序(默认)。
  • 迭代器遍历中序,有序。
  • 可以修改 mapped_type(即 value),不能修改 key。

3.3 构造与迭代器

与 set 类似,支持 initializer_list、区间、拷贝等。

map<string, string> dict = {
    {"left", "左边"},
    {"right", "右边"},
    {"insert", "插入"},
    {"sort", "排序"}
};

迭代器访问:

auto it = dict.begin();
while (it != dict.end()) {
    cout << it->first << ": " << it->second << endl;  // it->first 即 key
    ++it;
}

范围 for:

for (const auto& kv : dict) {
    cout << kv.first << ": " << kv.second << endl;
}

3.4 增删查接口

操作接口说明
插入insert({key, value})返回 pair<iterator,bool>,key 存在则失败
查找find(key)返回迭代器,可修改 value
计数count(key)返回 0 或 1(multimap 可>1)
删除erase(key)删除所有 key,返回删除个数
删除erase(pos)删除迭代器位置
区间删除erase(first,last)删除 [first,last)
下界/上界lower_bound/upper_bound同 set

3.5 核心:operator[] —— 插入+查找+修改三合一

operator[]是 map 最强大的接口,其内部实现等价于:

mapped_type& operator[](const key_type& k) {
    // 插入 {k, mapped_type()},如果已存在则返回已有迭代器
    pair<iterator, bool> ret = insert({k, mapped_type()});
    // 返回 value 的引用
    return ret.first->second;
}

行为:

  • 如果k不存在,插入{k,默认值},并返回该默认值的引用,你可以通过 = 赋值。
  • 如果k存在,直接返回对应 value 的引用,你可以读取或修改。

因此 [] 可以用于:

  • 插入加修改:dict["new"] = "新的";
  • 插入:dict["new"]
  • 查找:cout << dict["left"];(但如果不存在,会插入默认值,注意副作用)
  • 修改:dict["left"] = "左边,剩余";

⚠️ 注意:使用 [] 查找时,若 key 不存在会插入默认 value,可能不是你想要的行为,通常用 find 来纯粹查找。

3.6 综合示例

3.7multimap —— 允许 key 冗余

multimap与map的区别就是 key 可重复,因此:

  • insert总是成功。
  • find返回中序第一个。
  • count返回实际个数。
  • erase(key)删除所有该 key。
  • 不支持operator[],因为 key 不唯一,无法确定返回哪个 value。

四、总结

容器去重key 可修改value 可修改是否支持 []应用场景
set是否(整体不可改)—否快速去重、存在性判断
multiset否否—否允许重复的有序集合
map是否是是字典、映射、统计
multimap否否是否一对多映射(如一个key对应多个value

核心要点:

  • 底层红黑树,增删查 O(log N)。
  • 迭代器遍历有序。
  • set和map的 key 不可修改,但 map 的 value 可以修改。
  • operator[]是 map 的利器,但要小心副作用。
  • 多利用lower_bound/upper_bound做区间操作。

到此这篇关于C++ STL 中的 set 和 map 完全指南:从入门到实战的文章就介绍到这了,更多相关C++ STL set 和 map 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 实例讲解C++编程中对设计模式中的原型模式的使用

    实例讲解C++编程中对设计模式中的原型模式的使用

    这篇文章主要介绍了C++编程中对设计模式中的原型模式的使用实例,包括原型模式中对C++的深拷贝和浅拷贝的处理,需要的朋友可以参考下
    2016-03-03
  • 一文掌握scanf的用法实例小结

    一文掌握scanf的用法实例小结

    scanf的基本用法除了常规的输入操作外还有一些特殊的用法,使用这些用法可以很方便的在输入中读取想要的数据,这篇文章主要介绍了scanf的用法,需要的朋友可以参考下
    2023-12-12
  • c++ 入门——浅析构造函数和析构函数

    c++ 入门——浅析构造函数和析构函数

    这篇文章主要介绍了c++ 浅析构造函数和析构函数的相关资料,帮助大家入门c++ 编程,感兴趣的朋友可以了解下
    2020-08-08
  • C++深入浅出讲解隐藏this指针的用法

    C++深入浅出讲解隐藏this指针的用法

    在C++中,每一个对象都能通过this指针来访问自己的地址。this 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象
    2022-05-05
  • C++三元表达式详情

    C++三元表达式详情

    这篇文章主要介绍了C++三元表达式,文章围绕C++三元表达式的相关资料展开详细内容,需要的朋友可以参考一下,希望多你有所帮助
    2021-11-11
  • C++如何获取当前系统时间及格式化输出

    C++如何获取当前系统时间及格式化输出

    这篇文章主要介绍了C++如何获取当前系统时间及格式化输出的实例代码,主要用到time()及strftime()函数,通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-02-02
  • 如何判断一个数是否为2的幂次方?若是,并判断出来是多少次方?

    如何判断一个数是否为2的幂次方?若是,并判断出来是多少次方?

    本篇文章是对如何判断一个数是否为2的幂次方?若是,并判断出来是多少次方的实现方法,进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C语言找出数组中的特定元素的算法解析

    C语言找出数组中的特定元素的算法解析

    这篇文章主要介绍了C语言中找出数组中特定元素的算法解析,包括找出数组中两个只出现一次的数字的方法,需要的朋友可以参考下
    2016-03-03
  • Microsoft Visual C++进行调试的方法实现

    Microsoft Visual C++进行调试的方法实现

    VS功能极其强大,使用极其便利,本文主要介绍了Microsoft Visual C++进行调试的方法实现,具有一定的参考价值,感兴趣的可以了解一下
    2024-06-06
  • C++从零配置ffmpeg的详细过程

    C++从零配置ffmpeg的详细过程

    本博文源于笔者正在做的任务,将视频mp4每帧转换成jpg格式,前提是需要配置ffmpeg,因此本文从0下载教读者如何配置ffmpeg,文中通过图文结合的方式给大家介绍得非常详细,需要的朋友可以参考下
    2024-03-03

最新评论