当前位置: 首页 > news >正文

STL详解 - map和set

目录

一、关联式容器概述

二、键值对 

三、树形结构的关联式容器

(一)set

1. set的介绍

2. set的使用

(1)模板参数列表

(2)构造函数

(3)迭代器函数

(4)容量函数

(5)修改操作函数

(6)常用接口

(7)使用举例

(二)map

1. map的介绍

2. map的使用

(1)模板参数说明

(2)构造函数 

(3)迭代器函数

(4)容量与元素访问函数

(5)元素修改函数

(6)常用接口

(7)使用示例

(三)multiset

1. multiset的介绍

2. multiset的使用

(四)multimap

1. multimap的介绍

2. multimap的使用

四、容器对比


一、关联式容器概述

在STL中,容器分为两大类别:

  1. 序列式容器:线性数据结构(vector/list/deque等),存储元素本身

  2. 关联式容器:存储<key,value>键值对,基于红黑树实现,具有高效检索特性

主要区别:

  • 存储内容:键值对 vs 独立元素

  • 检索效率:O(logN) vs O(N)

  • 元素顺序:自动排序 vs 插入顺序

二、键值对 

        键值对是用来表示具有一一对应关系的一种结构,一般包含两个成员变量 key value,key 代表键值,value 表示与 key 对应的信息。例如建立英汉互译字典,英文单词与其中文含义是一一对应的。

在 SGI-STL 中,键值对的定义如下:

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) {}
};

三、树形结构的关联式容器

        STL 中实现了两种不同结构的关联式容器:树型结构与哈希结构。树型结构的关联式容器主要有四种:mapsetmultimapmultiset,它们都使用平衡搜索树(红黑树)作为底层结构,容器中的元素是有序的序列。

(一)set

1. set的介绍

set文档介绍

翻译: 

  • set 是按照一定次序存储元素的容器。
  • 在 set 中,元素的 value 也标识它(value就是 key,类型为 T),并且每个 value 必须唯一的。set 中的元素不能在容器中修改(元素总是const),但可以从容器中插入或删除它们。
  • 在内部,set 中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序。
  • set 容器通过key 访问单个元素的速度通常比 unordered_set容器慢,但它们允许按顺序对子集进行直接迭代。
  • set 在底层是用二叉搜索树(红黑树)实现的。

注意:

  • 与 map/multimap 不同,map/multimap 存储的是真正的键值对<key, value>,set 中只放 value,但在底层实际存放的是由<value, value>构成的键值对。
  • set 中插入元素时,只需插入 value 即可,无需构造键值对。
  • set 中的元素不可重复,(因此可以使用set进行去重)。
  • 使用set的迭代器遍历set中的元素,可以得到有序序列。
  • set中的元素默认按照小于来比较。
  • set 中查找某个元素的时间复杂度为 O(log₂n)。
  • set 中的元素不允许修改,因为其底层是二叉搜索树(红黑树)。

2. set的使用

(1)模板参数列表

参数名称说明
Tset 中存放元素的类型,底层存储 <value, value> 的键值对。
Compare比较函数对象的类型,默认根据 less<T> 按元素是否小于另一元素排序。
Alloc用于元素分配和管理空间的分配器,默认使用 STL 提供的空间配置器。
(2)构造函数
构造函数说明
set (const Compare& comp = Compare(), const Allocator& alloc = Allocator())构造空的 setcomp 用于比较,alloc 用于空间管理。
set (InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& alloc = Allocator())使用范围 [first, last) 中的元素构造 setcomp 和 alloc 同上。
set (const set<Key, Compare, Allocator>& x)拷贝构造,用 x 的内容初始化新 set
(3)迭代器函数
迭代器函数说明
begin()返回指向起始位置元素的迭代器。
end()返回指向最后一个元素后面位置的迭代器。
cbegin()返回指向起始位置元素的 const 迭代器。
cend()返回指向最后一个元素后面位置的 const 迭代器。
rbegin()返回指向第一个元素的反向迭代器,即指向末尾的迭代器。
rend()返回指向最后一个元素下一个位置的反向迭代器,即指向起始位置的迭代器。
crbegin()返回指向第一个元素的反向 const 迭代器,即指向 cend()
crend()返回指向最后一个元素下一个位置的反向 const 迭代器,即指向 cbegin()
(4)容量函数
容量函数说明
empty()检测 set 是否为空,若为空返回 true,否则返回 false
size()返回 set 中有效元素的个数。
(5)修改操作函数
修改操作函数说明
insert(const value_type& x)插入元素 x,实际插入的是 <x, x> 构成的键值对。若插入成功,返回包含该元素在 set 中位置和 true 的 pair;若失败(x 已存在),返回包含 x 在 set 中位置和 false 的 pair
erase(iterator position)删除 position 位置上的元素。
erase(const key_type& x)删除值为 x 的元素,返回删除的元素个数。
erase(iterator first, iterator last)删除 [first, last) 区间中的元素。
swap(set<Key, Compare, Allocator>& st)交换两个 set 中的元素。
clear()清空 set 中的元素。
find(const key_type& x)返回值为 x 的元素的位置。
count(const key_type& x)返回值为 x 的元素的个数。
(6)常用接口
// 构造
set<int> s1;                     // 空set
set<int> s2(arr, arr+arrSize);   // 数组构造// 插入删除
s.insert(10);        // 插入元素
s.erase(20);         // 删除元素// 查找
auto pos = s.find(5);    // 返回迭代器
int cnt = s.count(5);    // 存在返回1,否则0// 容量
bool isEmpty = s.empty();
size_t size = s.size();
(7)使用举例
#include <iostream>
#include <set>
using namespace std;int main() 
{//set<int> s;//s.insert(5);//s.insert(3);//......int arr[] = { 5,3,7,1,9,2,5,3 }; // 包含重复元素set<int> s(arr, arr + sizeof(arr) / sizeof(int));cout << "Set size: " << s.size() << endl; // 输出6(自动去重)// 正向遍历(自动排序)cout << "Ascending: ";for (auto& num : s) {cout << num << " "; // 1 2 3 5 7 9}// 反向遍历cout << "\nDescending: ";for (auto it = s.rbegin(); it != s.rend(); ++it) {cout << *it << " "; // 9 7 5 3 2 1}// 查找测试cout << "\nCount of 3: " << s.count(3) << endl;  // 1cout << "Count of 4: " << s.count(4) << endl;    // 0
}

(二)map

1. map的介绍

map文档介绍

翻译:

  • map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。
  • 在map中,键值key通常用于排序和唯一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为pair:
  • typedef pair<const key, T> value_type;
  • 在内部,map中的元素总是按照键值key进行比较排序的。
  • map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
  • map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
  • map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。

注意:

  • map 中的元素是键值对。
  • key 是唯一的,且不能修改。
  • 默认按小于的方式对 key 进行比较。
  • 遍历时可得到按 key 排序的序列。
  • 底层为平衡搜索树(红黑树),查找效率高,时间复杂度为 O(log₂N)。
  • 支持 [ ] 操作符,operator[ ] 中实际进行插入查找。
2. map的使用
(1)模板参数说明

参数名称说明
Key键值对中 key 的类型。
T键值对中 value 的类型。
Compare比较器的类型,默认按小于比较,对于内置类型元素一般无需传递,自定义类型时需显式传递比较规则(一般用函数指针或仿函数)。
Alloc通过空间配置器申请底层空间,默认使用标准库提供的空间配置器,除非用户另有指定。

(2)构造函数 
构造函数说明
map()构造一个空的 map
(3)迭代器函数
迭代器函数说明
begin()返回指向首元素的迭代器。
end()返回指向最后一个元素的下一个位置的迭代器。
cbegin()返回指向首元素的 const 迭代器,指向的元素不能修改。
cend()返回指向最后一个元素的下一个位置的 const 迭代器,指向的元素不能修改。
rbegin()返回反向迭代器,初始指向最后一个元素(即 end 位置),其 ++ 操作向首元素方向移动。
rend()返回反向迭代器,初始指向第一个元素的前面(即 begin 位置),其 -- 操作向尾元素方向移动。
crbegin()返回反向 const 迭代器,功能同 rbegin(),但指向的元素不能修改。
crend()返回反向 const 迭代器,功能同 rend(),但指向的元素不能修改。
(4)容量与元素访问函数
容量与元素访问函数说明
empty()检测 map 是否为空,若为空返回 true,否则返回 false
size()返回 map 中有效元素的个数。
operator[]通过 key 获取对应的 value。当 key 不存在时,会用默认 value 构造键值对并插入,然后返回该默认 value。
at()与 operator[] 类似,但当 key 不存在时直接抛异常。
(5)元素修改函数
元素修改函数说明
insert(const value_type& x)插入键值对 x,返回值包含新插入元素的位置和是否插入成功。
erase(iterator position)删除 position 位置上的元素。
erase(const key_type& x)删除键值为 x 的元素。
erase(iterator first, iterator last)删除 [first, last) 区间中的元素。
swap(map<Key, T, Compare, Allocator>& mp)交换两个 map 中的元素。
clear()清空 map 中的元素。
find(const key_type& x)查找键值为 x 的元素,找到返回该元素的位置的迭代器,否则返回 end
count(const key_type& x)返回键值为 x 的键值在 map 中的个数,由于 map 中 key 唯一,返回值要么为 0,要么为 1。
(6)常用接口
map<string, int> m;// 插入
m.insert(make_pair("apple", 5));
m["banana"] = 3;  // 使用[]插入// 访问
int num = m["apple"];  // 不存在会插入默认值
int num2 = m.at("apple"); // 不存在抛异常// 删除
m.erase("banana");// 查找
auto pos = m.find("apple");
if(pos != m.end()) {cout << pos->first << ": " << pos->second;
}
(7)使用示例
#include <iostream>
#include <string>
#include <map>
using namespace std;int main() 
{map<string, string> m;// 向map中插入元素的几种方式pair<string, string> kv("peach", "桃子");m.insert(kv); // 有名对象m.insert(pair<string, string>("peach", "桃子")); // 匿名对象m.insert(make_pair("banana", "香蕉")); // 函数模板m.insert({ "apple", "苹果" }); // 隐式类型转换m["apple"] = "苹果"; // 若key不存在则插入,默认构造value然后赋值// 用迭代器遍历map,可得到按key排序的序列cout << "Map contents:" << endl;for (auto& e : m) {cout << e.first << " ---> " << e.second << endl;}// 尝试插入已存在的keyauto ret = m.insert(make_pair("peach", "桃色"));if (!ret.second) {cout << "Key 'peach' already exists: " << ret.first->first << " ---> "<< ret.first->second << " Insert failed" << endl;}// 删除key为"apple"的元素m.erase("apple");if (m.count("apple") == 0) {cout << "Apple has been erased" << endl;}// 修改值m["banana"] = "大香蕉"; // 修改已有项// 遍历输出cout << "Map after update:" << endl;for (auto& entry : m) {cout << entry.first << " => " << entry.second << endl;}// 危险操作:访问不存在的键cout << "Peach: " << m["peach"] << endl; // 插入空值// m.at("watermelon") = "西瓜"; // 抛出异常// 安全查找auto pos = m.find("pear");if (pos != m.end()) {cout << "Found pear: " << pos->second << endl;}else {cout << "Pear not found!" << endl;}
}

(三)multiset

1. multiset的介绍

multiset文档介绍

翻译:

  • multiset是按照特定顺序存储元素的容器,其中元素是可以重复的。
  • 在multiset中,元素的value也会识别它(因为multiset中本身存储的就是<value, value>组成的键值对,因此value本身就是key,key就是value,类型为T). multiset元素的值不能在容器中进行修改(因为元素总是const的),但可以从容器中插入或删除。
  • 在内部,multiset中的元素总是按照其内部比较规则(类型比较)所指示的特定严格弱排序准则进行排序。
  • multiset容器通过key访问单个元素的速度通常比unordered_multiset容器慢,但当使用迭代器遍历时会得到一个有序序列。
  • multiset底层结构为二叉搜索树(红黑树)。

注意:

  • multiset在底层中存储的是<value, value>的键值对
  • mtltiset的插入接口中只需要插入即可
  • 与set的区别是,multiset中的元素可以重复,set是中value是唯一的
  • 使用迭代器对multiset中的元素进行遍历,可以得到有序的序列
  • multiset中的元素不能修改
  • 在multiset中找某个元素,时间复杂度为O(log₂N)
  • multiset的作用:可以对元素进行排序

2. multiset的使用

#include <set>
#include <iostream>
using namespace std;int main()
{int array[] = { 2, 1, 3, 3, 9, 6, 6, 0, 5, 8, 4, 7 };multiset<int> s(array, array + sizeof(array) / sizeof(array[0]));// 遍历multiset,可得到按升序排序且允许重复元素的序列for (auto& e : s)cout << e << " ";cout << endl;
}

(四)multimap

1. multimap的介绍

multimap文档介绍

翻译:

  • Multimaps是关联式容器,它按照特定的顺序,存储由key和value映射成的键值对<key,value>,其中多个键值对之间的key是可以重复的。
  • 在multimap中,通常按照key排序和唯一地标识元素,而映射的value存储与key关联的内容。key和value的类型可能不同,通过multimap内部的成员类型value_type组合在一起,value_type是组合key和value的键值对:
  • typedef pair<const Key, T> value_type;
  • 在内部,multimap中的元素总是通过其内部比较对象,按照指定的特定严格弱排序标准对key进行排序的。
  • multimap通过key访问单个元素的速度通常比unordered_multimap容器慢,但是使用迭代器直接遍历multimap中的元素可以得到关于key有序的序列。
  • multimap在底层用二叉搜索树(红黑树)来实现。

注意: 

  • map中的key是唯一的,而multimap中key是可以重复的。
  • 不支持 [ ] 运算符。

2. multimap的使用

#include <map>
#include <iostream>
using namespace std;int main() 
{multimap<string, string> contacts;contacts.insert(make_pair("John", "123-4567"));contacts.insert(make_pair("John", "123-4568"));contacts.insert(make_pair("Alice", "555-1234"));// 查找所有John的联系方式auto range = contacts.equal_range("John");cout << "John's numbers:\n";for (auto it = range.first; it != range.second; ++it) {cout << it->second << endl;}
}

四、容器对比

特性setmultisetmapmultimap
存储元素唯一值可重复值唯一键可重复键
数据组织值即键值即键键值对键值对
[ ]运算符不支持不支持支持不支持
典型应用场景去重/排序排序统计字典/映射多映射

相关文章:

  • 性能分析硬核特训 · Perf 全面指南:内核实例 + 原理实战 + 面试答疑
  • JAVA SE(9)——多态
  • OpenCV进阶操作:角点检测
  • n8n系列(1)初识n8n:工作流自动化平台概述
  • 基于 GO 语言的 Ebyte 勒索软件——简要分析
  • 【Vue】全局事件总线 TodoList 事件总线
  • HTML07:表格标签
  • 全球10公里分辨率的玉米、小麦、水稻和大豆生产栅格数据集(2010-2020)
  • K8S - Volume 与 PersistentVolume - 容器存储与数据持久化
  • 通过TinyML为语音助手赋能,推动以用户为中心的创新和现实世界应用
  • vue3+ts+自定义指令,生产页面水印
  • Linux云服务器配置git开发环境
  • 计算机操作系统 - 多级反馈队列比例份额
  • deeplabv3+街景图片语义分割,无需训练模型,看不懂也没有影响,直接使用,cityscapes数据集_23
  • 搭建spark yarn 模式的集群
  • 武汉火影数字|数字规划馆打造沉浸式数字化互动内容
  • 5月6日日记
  • n8n 与智能体构建:开发自动化 AI 作业的基础平台
  • 使用Java实现任务调度:从Timer到Quartz
  • 【CPU占用率查看】
  • 上海一中院一审公开开庭审理被告人胡欣受贿案
  • 男子煎服15克山豆根中毒送医,医生:不能盲目相信偏方
  • 巴基斯坦军方称印度袭击已致26死46伤
  • 央行:增加支农支小再贷款额度3000亿元
  • 詹丹|高考语文阅读题设计和答案拟制的一些缺憾
  • 巴基斯坦宣布关闭全国空域48小时