跳到主要内容 C++ STL set 与 map 容器详解 | 极客日志
C++ 算法
C++ STL set 与 map 容器详解 C++ STL 中 set 和 map 容器的使用。set 基于红黑树实现,用于 key 搜索,支持有序存储且自动去重;map 同样基于红黑树,用于 key/value 搜索,key 有序。文章涵盖了构造方式、迭代器遍历、增删查操作(insert, find, erase, count, lower_bound, upper_bound)以及 multiset/multimap 的差异。通过代码示例演示了 pair 类型的使用、operator[] 的功能特性,并结合 LeetCode 题目展示了在链表判环和前 K 个高频单词统计中的实际应用。
安卓系统 发布于 2026/3/29 更新于 2026/4/18 4 浏览序列式容器和关联式容器是 STL 的重要组成部分。序列式容器(如 vector、list)逻辑结构为线性序列,元素按存储位置顺序保存和访问。关联式容器(如 map、set)逻辑结构通常是非线性的,元素按关键字保存和访问。
#include <set>
#include <map>
1. set 的使用
1.1 set 类的介绍 set 的声明如下,T 就是 set 底层关键字的类型。set 默认要求 T 支持小于比较,如果不支持或者想按自己的需求走可以自行实现仿函数传给第二个模板参数。set 底层是用红黑树实现,增删查效率是 O(logN),迭代器遍历是走的搜索树的中序,所以是有序的。
template < class T ,
class Compare = less<T>,
class Alloc = allocator<T>
> class set;
1.2 set 的构造和迭代器 set 支持正向和反向迭代遍历,遍历默认按升序顺序。set 的 iterator 和 const_iterator 都不支持迭代器修改数据,修改关键字数据会破坏底层搜索树的结构。
explicit set (const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()) ;
template <class InputIterator > set (InputIterator first, InputIterator last, const key_compare& comp = key_compare (), const allocator_type& = allocator_type ());
set (const set& x);
set (initializer_list<value_type> il, const key_compare& comp = key_compare (), const allocator_type& alloc = allocator_type ());
iterator begin () ; iterator end () ;
reverse_iterator rbegin () ; reverse_iterator rend () ;
1.3 set 的增删查 Member types
key_type -> The first template parameter (T)
value_type -> The first template parameter (T)
pair<iterator,bool > insert (const value_type& val) ;
void insert (initializer_list<value_type> il) ;
template <class InputIterator > void insert (InputIterator first, InputIterator last) ;
iterator find (const value_type& val) ;
size_type count (const value_type& val) const ;
iterator erase (const_iterator position) ;
size_type erase (const value_type& val) ;
iterator erase (const_iterator first, const_iterator last) ;
iterator lower_bound (const value_type& val) const ;
iterator upper_bound (const value_type& val) const ;
1.4 代码练习
1.4.1 set 的构造,插入与删除 int main () {
set<int > s;
s.insert (19 );
s.insert (5 );
s.insert (10 );
int arr[] = { 2 ,6 ,4 ,8 ,5 };
s.insert (arr, arr + 4 );
for (auto e : s) cout << e << " " ;
cout << endl;
set<string> str ({ "hello" , "world" , "love" }) ;
for (auto & e : str) {
cout << e << " " ;
}
cout << endl;
int n;
cin >> n;
int num = s.erase (n);
if (num == 0 ) cout << n << "不存在" << endl;
else cout << "删除成功" << endl;
for (auto e : s) cout << e << " " ;
return 0 ;
}
set<int > ::iterator it = s2. begin ();
while (it != s2. end ()) {
cout << *it << " " ;
*it = 1 ;
it++;
}
1.4.2 set 的 find 的使用样例,与 erase 结合 int main () {
set<int > s = { 4 ,2 ,7 ,2 ,8 ,5 ,9 };
for (auto e : s) {
cout << e << " " ;
}
cout << endl;
auto pos = s.find (7 );
if (pos != s.end ()) {
s.erase (pos);
}
for (auto e : s) {
cout << e << " " ;
}
cout << endl;
int x;
cout << "删除的数" << endl;
cin >> x;
auto pos1 = find (s.begin (), s.end (), x);
while (pos1 != s.end ()) {
cout << x << "存在,并删除成功!" << endl;
break ;
}
for (auto e : s) {
cout << e << " " ;
}
cout << endl;
int y;
cin >>y;
if (s.count (y)) {
cout << y << "在!" << endl;
} else {
cout << y << "不存在!" << endl;
}
return 0 ;
}
我们通过了 set 自带的查找以及算法库的查找,然后也用了 set 中的 count 进行了间接查找,通过数据的次数进行判断。
1.4.3 set 获取区间值函数的应用
iterator lower_bound (const value_type& val) const ;
iterator upper_bound (const value_type& val) const ;
int main () {
set<int >s;
for (int i = 1 ; i <= 9 ; i++) {
s.insert (i * 10 );
}
auto it = s.begin ();
while (it != s.end ()) {
cout << *it << " " ;
it++;
}
cout << endl;
auto itlow = s.lower_bound (30 );
auto itup = s.upper_bound (70 );
s.erase (itlow, itup);
for (auto e : s) {
cout << e << " " ;
}
return 0 ;
}
1.5 multiset 和 set 的差异 multiset 和 set 的使用基本完全类似,主要区别点在于 multiset 支持值冗余,那么 insert/find/count/erase 都围绕着支持值冗余有所差异,具体参看下面的样例代码理解。
int main () {
multiset<int > s = { 4 ,2 ,7 ,2 ,4 ,8 ,4 ,5 ,4 ,9 };
auto it = s.begin ();
while (it != s.end ()) {
cout << *it << " " ;
++it;
}
cout << endl;
int x;
cin >> x;
auto pos = s.find (x);
while (pos != s.end () && *pos == x) {
cout << *pos << " " ;
++pos;
}
cout << endl;
cout << s.count (x) << endl;
s.erase (x);
for (auto e : s) {
cout << e << " " ;
}
cout << endl;
return 0 ;
}
1.6 set 强化练习 在数据结构链表篇,我们采用了双指针的算法,先定义快慢指针判断链表是否带环,如果相遇,则链表有环存在,然后定义一个 meet 指针在相遇的位置,头结点和 meet 同时往前移动,相遇的时候就是环的位置。
在学习了 set 之后,我们可以将节点存放到 set 中,当 set 中节点个数重复的时候说明有环的存在,就可以返回该节点位置。
class Solution { public :
ListNode *detectCycle (ListNode *head) {
set<ListNode*>s;
ListNode*cur=head;
while (cur){
if (s.count (cur)) return cur;
else s.insert (cur);
cur=cur->next;
}
return NULL ;
}
};
2. map 的使用
2.1 map 类的介绍 map 的声明如下,Key 就是 map 底层关键字的类型,T 是 map 底层 value 的类型,set 默认要求 Key 支持小于比较,如果不支持或者需要的话可以自行实现仿函数传给第二个模板参数,map 底层存储数据的内存是从空间配置器申请的。一般情况下,我们都不需要传后两个模板参数。map 底层是用红黑树实现,增删查改效率是 O(logN),迭代器遍历是走的中序,所以是按 key 有序顺序遍历的。
template < class Key ,
class T ,
class Compare = less<Key>,
class Alloc = allocator<pair<const Key,T> >
> class map;
2.2 pair 类型 map 底层的红黑树节点中的数据,使用 pair<Key, T>存储键值对数据。
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) );
}
2.3 map 的构造 map 的支持正向和反向迭代遍历,遍历默认按 key 的升序顺序,因为底层是二叉搜索树,迭代器遍历走的中序;支持迭代器就意味着支持范围 for,map 支持修改 value 数据,不支持修改 key 数据,修改关键字数据,破坏了底层搜索树的结构。
explicit map (const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()) ;
template <class InputIterator > map (InputIterator first, InputIterator last, const key_compare& comp = key_compare (), const allocator_type& = allocator_type ());
map (const map& x);
map (initializer_list<value_type> il, const key_compare& comp = key_compare (), const allocator_type& alloc = allocator_type ());
iterator begin () ; iterator end () ;
reverse_iterator rbegin () ; reverse_iterator rend () ;
2.4 map 的增删查 map 增接口,插入的 pair 键值对数据,跟 set 所有不同,但是查和删的接口只用关键字 key 跟 set 是完全类似的,不过 find 返回 iterator,不仅仅可以确认 key 在不在,还找到 key 映射的 value,同时通过迭代还可以修改 value。
Member types
key_type -> The first template parameter (Key)
mapped_type -> The second template parameter (T)
value_type -> pair<const key_type,mapped_type>
pair<iterator,bool > insert (const value_type& val) ;
void insert (initializer_list<value_type> il) ;
template <class InputIterator > void insert (InputIterator first, InputIterator last) ;
iterator find (const key_type& k) ;
size_type count (const key_type& k) const ;
iterator erase (const_iterator position) ;
size_type erase (const key_type& k) ;
iterator erase (const_iterator first, const_iterator last) ;
iterator lower_bound (const key_type& k) ;
const_iterator lower_bound (const key_type& k) const ;
2.5 map 的数据修改 前面提到 map 支持修改 mapped_type 数据,不支持修改 key 数据,修改关键字数据,破坏了底层搜索树的结构。map 第一个支持修改的方式时通过迭代器,迭代器遍历时或者 find 返回 key 所在的 iterator 修改,map 还有一个非常重要的修改接口 operator[],但是 operator[] 不仅仅支持修改,还支持插入数据和查找数据,所以他是一个多功能复合接口。
需要注意从内部实现角度,map 这里把我们传统说的 value 值,给的是 T 类型,typedef 为 mapped_type。而 value_type 是红黑树结点中存储的 pair 键值对值。
Member types
key_type -> The first template parameter (Key)
mapped_type -> The second template parameter (T)
value_type -> pair<const key_type,mapped_type>
iterator find (const key_type& k) ;
文档中对 insert 返回值的说明:
The single element versions (1) return a pair, with its member pair::first set to an iterator pointing to either the newly inserted element or to the element with an equivalent key in the map. The pair::second element in the pair is set to true if a new element was inserted or false if an equivalent key already existed.
insert 插入一个 pair<key, T>对象
1、如果 key 已经在 map 中,插入失败,则返回一个 pair<iterator,bool>对象,返回 pair 对象 first 是 key 所在结点的迭代器,second 是 false
2、如果 key 不在在 map 中,插入成功,则返回一个 pair<iterator,bool>对象,返回 pair 对象 first 是新插入 key 所在结点的迭代器,second 是 true
也就是说无论插入成功还是失败,返回 pair<iterator,bool>对象的 first 都会指向 key 所在的迭代器
那么也就意味着 insert 插入失败时充当了查找的功能,正是因为这一点,insert 可以用来实现 operator[]
需要注意的是这里有两个 pair,不要混淆了,一个是 map 底层红黑树节点中存的 pair<key, T>,另一个是 insert 返回值 pair<iterator,bool>
mapped_type& operator [] (const key_type& k);
mapped_type& operator [] (const key_type& k) {
pair<iterator, bool > ret = insert ({ k, mapped_type () });
iterator it = ret.first;
return it->second;
}
2.6 代码练习
2.6.1 构造遍历及增删查使用样例 int main () {
map<string, string>dict{ { "left" ,"左边" },{"right" , "右边" }, { "insert" , "插入" }};
pair<string, string>kv ( "first" ,"第一个" );
dict.insert (kv);
dict.insert (pair<string, string>{"second" , "第二个" });
dict.insert (make_pair ("third" , "第三个" ));
dict.insert ({ "string" ,"字符串" });
dict.insert ({ "left" , "自动的 xxxx" });
map<string, string>::iterator it = dict.begin ();
while (it != dict.end ()) {
cout << it->first << ":" << it->second << endl;
it++;
}
dict.insert ({ "string" ,"字符串串" });
cout << "输入查询的单词" << endl;
string s;
while (cin >> s) {
auto ret = dict.find (s);
if (ret != dict.end ()) {
cout << "->" << ret->second << endl;
break ;
} else {
cout << "没有该单词,请重新输入!" << endl;
}
}
dict.erase ("second" );
auto it1 = dict.find ("first" );
dict.erase (it1);
for (auto s : dict) {
cout << s.first << "->" << s.second << endl;
}
return 0 ;
}
2.6.2 map 的迭代器和 [] 功能 前提引入
如果我们要统计一个物件的数目,我们就可以采 map 结构,例如下面代码:
int main () {
string arr[] = { "苹果" , "西瓜" , "苹果" , "西瓜" , "苹果" , "苹果" , "西瓜" , "苹果" , "香蕉" , "苹果" , "香蕉" };
map<string, int >countMap;
for (auto &s : arr) {
auto pos = countMap.find (s);
if (pos == countMap.end ()) {
countMap.insert ({ s,1 });
} else pos->second++;
}
for (auto s : countMap) {
cout << s.first << "->" << s.second << endl;
}
return 0 ;
}
先查找水果在不在 map 中 1、不在,说明水果第一次出现,则插入{水果,1}2、在,则查找到的节点中水果对应的次数++
这里使用常见的 insert 来进行数数,通过对 [] 的认识,我们可以利用它的特点同样的来实现计数的功能。
int main () {
string arr[] = { "苹果" , "西瓜" , "苹果" , "西瓜" , "苹果" , "苹果" , "西瓜" , "苹果" , "香蕉" , "苹果" , "香蕉" };
map<string, int >cm;
for (const auto & str : arr) {
cm[str]++;
}
for (auto & s : cm) {
cout << s.first << "->" << s.second << endl;
}
return 0 ;
}
int main () {
map<string, string> dict;
dict.insert (make_pair ("sort" , "排序" ));
dict["insert" ];
dict["left" ] = "左边" ;
dict["left" ] = "左边、剩余" ;
cout << dict["left" ] << endl;
for (auto s : dict) {
cout << s.first << "->" << s.second << endl;
}
return 0 ;
}
2.6 multimap 和 map 的差异 multimap 和 map 的使用基本完全类似,主要区别点在于 multimap 支持关键值 key 冗余,那么 insert/find/count/erase 都围绕着支持关键值 key 冗余有所差异,这里跟 set 和 multiset 完全一样,比如 find 时,有多个 key,返回中序第一个。其次就是 multimap 不支持 [],因为支持 key 冗余,[] 就只能支持插入了,不能支持修改。
2.7 map 强化练习 解决思路 1 用排序找前 k 个单词,因为 map 中已经对 key 单词排序过,也就意味着遍历 map 时,次数相同的单词,字典序小的在前面,字典序大的在后面。那么将数据放到 vector 中用一个稳定的排序就可以实现上面特殊要求,但是 sort 底层是快排,是不稳定的,所以要用 stable_sort,是稳定的。
class Solution { public :
static bool compare (const pair<string,int >&s1,const pair<string,int >&s2) {
return s1. second>s2. second;
}
vector<string> topKFrequent (vector<string>& words, int k) {
map<string,int > m;
for (auto &s:words){
m[s]++;
}
vector<pair<string,int >> v (m.begin (),m.end ());
stable_sort (v.begin (),v.end (),compare);
vector<string>v1;
for (int i=0 ;i<k;i++){
v1. push_back (v[i].first);
}
return v1;
}
};
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 > countMap;
for (auto & e : words) {
countMap[e]++;
}
vector<pair<string, int >> v (countMap.begin (),countMap.end ());
stable_sort (v.begin (), v.end (), Compare ());
vector<string> strV;
for (int i = 0 ; i < k; ++i) {
strV.push_back (v[i].first);
}
return strV;
}
};
解决思路 2
将 map 统计出的次数的数据放到 vector 中排序,或者放到 priority_queue 中来选出前 k 个。利用仿函数强行控制次数相等的,字典序小的在前面。
class Solution { public :
static bool compare (const pair<string,int >&s1,const pair<string,int >&s2) {
return s1. second>s2. second ||(s1. second==s2. second && s1.f irst<s1.f irst);
}
vector<string> topKFrequent (vector<string>& words, int k) {
map<string,int > m;
for (auto &s:words){
m[s]++;
}
vector<pair<string,int >> v (m.begin (),m.end ());
sort (v.begin (),v.end (),compare);
vector<string>v1;
for (int i=0 ;i<k;i++){
v1. push_back (v[i].first);
}
return v1;
}
};
微信扫一扫,关注极客日志 微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,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
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online