文章目录
- 前言
- 一、set容器
- 二、multiset
- 三、map
- 四、multimap
前言
1、set、map、 multiset、 multimap都是基于红黑树实现的容器。
2、set、multiset都使用头文件#include<set>
,map、multimap都是使用头文件#include<map>
一、set容器
1、set容器的介绍
C++标准库中的set容器是一种关联容器,它存储的元素是唯一的,并且以特定的顺序排列。set容器基于红黑树实现,这意味着它可以提供对数时间复杂度的插入、删除和查找操作。
2、set容器的特性
(1)唯一性:set中的每个元素都必须是唯一的,因为set通过其值来标识元素,而不是通过位置或索引。
(2)自动排序:set中的元素会根据其值自动排序。默认情况下,使用元素类型的<运算符进行排序,但可以指定自定义的比较函数来改变排序规则。
(3)对数时间复杂度:插入、删除和查找操作的时间复杂度为O(log n),其中n是容器中元素的数量。
(4)不支持直接元素访问:与vector或deque等序列容器不同,set不支持通过位置直接访问元素。要访问元素,必须使用迭代器或通过其值来查找。
(5) 与map/multimap不同,map/multimap中存储的是真正的键值对<key, value>,set中只放
value,但在底层实际存放的是由<value, value>构成的键值对
(6) set中插入元素时,只需要插入value即可,不需要构造键值对。
(7)不支持修改元素:因为set的设计初衷是为了维护一个唯一且自动排序的元素集合。如果允许直接修改 元素的值,那么可能会破坏set的这两个核心特性:
唯一性:set中的每个元素都必须是唯一的。如果允许修改元素的值,那么可能会使得原本唯一的元素变得不唯一,比如将一个元素修改为set中已存在的另一个元素的值。
自动排序:set中的元素会根据其值自动排序。如果允许修改元素的值,那么可能会破坏原有的排序顺序,使得set无法再按照预期的顺序来遍历或查找元素。
3、set容器使用
(1)模板参数:
T: set中存放元素的类型,实际在底层存储<value, value>的键值对。
Compare:set中元素默认按照小于来比较。
Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理
注意:一般只传第一个参数,如果需要按照特定条件排序可以自定义一个比较仿函数作为第二个参数,第三个基本不用传。
(2)常用构造、赋值接口
函数声明 | 功能 |
---|---|
set (const Compare& comp = Compare(), const Allocator& = Allocator() ); | 构造空的set |
set (InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& = Allocator() ); | 用[first, last)区间中的元素构造set |
set ( const set<Key,Compare,Allocator>& x); | set的拷贝构造 |
set& operator= (const set& x); | 进行赋值 |
使用:
//无参构造构造
set<int> set1;//迭代区间构造
vector<int> v = { 1,2,3,4,5 };
set<int> set2(v.begin(), v.end());//拷贝构造
set<int> set3(set2);//赋值
set<int> set4;
set4 = set3;
(3)常用迭代器接口
set迭代器是双向迭代器,只支持++或者–操作,不支持随机访问。
函数声明 | 功能 |
---|---|
iterator begin() | 返回第一个元素的迭代器 |
iterator end() | 返回最后一个元素的下一个位置的迭代器 |
使用:
void test02()
{//迭代区间构造vector<int> v = { 1,2,3,4,5 };set<int> set1(v.begin(), v.end());//迭代器//指向第一个元素的迭代器set<int>::iterator it1 = set1.begin();//指向最后一个元素的迭代器set<int>::iterator it2 = set1.end();//通过迭代器遍历while (it1 != it2){cout << *it1 << " ";it1++;}cout << endl;
}
(4)常用插入、删除、查找接口
函数声明 | 功能 |
---|---|
pair<iterator,bool> insert (const value_type& val); | 在set中插入元素x,实际插入的是<x, x>构成的键值对,如果插入成功,返回该元素在set中的位置,true>,如果插入失败,说明x在set中已经存在,返回<x在set中的位置,false> |
iterator erase (const_iterator position); | 删除set中position位置上的元素,返回值是一个迭代器,指向被删除元素之后的元素。如果 position 指向的是 set 中的最后一个元素,那么返回的迭代器将等于 set 的 end() 迭代器 |
size_type erase ( const key_type& x ) | 删除set中值为x的元素,返回删除的元素的个数 |
void clear() noexcept; | 清空 |
iterator find (const value_type& val); | 返回set中值为x的元素的位置,找不到返回end() |
使用:
//插入、删除、查找
void test03()
{//set<int> set1;//插入set1.insert(1);set1.insert(2);set1.insert(3);set1.insert(4);set1.insert(5);cout << "插入:";set<int>::iterator it1 = set1.begin();//通过迭代器遍历while (it1 != set1.end()){cout << *it1 << " ";it1++;}cout << endl;//通过迭代器遍历删除set<int> set2(set1);cout << "迭代器遍历删除:";set<int>::iterator it2 = set2.begin();//通过迭代器遍历while (it2 != set2.end()){it2 = set2.erase(it2);}cout << endl;cout << "键值删除:";set<int> set3(set1);set3.erase(1);set3.erase(3);set<int>::iterator it3 = set3.begin();//通过迭代器遍历while (it3 != set3.end()){cout << *it3 << " ";it3++;}cout << endl;//查找set<int> set4(set1);cout << "查找:";auto it4 = set4.find(2);if (it4 != set4.end())cout << *it4 << endl;elsecout << "找不到!" << endl;}
(5)常用大小相关接口
函数声明 | 功能 |
---|---|
size_type count ( const key_type& x ) const | 返回set中值为x的元素的个数 |
bool empty() const noexcept; | 判断是否为空 |
size_type size() const noexcept; | 返回元素个数 |
使用: |
void test04()
{vector<int> v = { 1,2,3,4,5 };set<int> set1(v.begin(), v.end());cout << "大小:" << set1.size() << endl;cout << "1的个数:" << set1.count(1) << endl;if (!set1.empty())cout << "不为空" << endl;elsecout << "为空" << endl;
}
二、multiset
1、multiset介绍
- multiset是按照特定顺序存储元素的容器,其中元素是可以重复的。
- 在multiset中,元素的value也会识别它(因为multiset中本身存储的就是<value, value>组成 的键值对,因此value本身就是key,key就是value,类型为T). multiset元素的值不能在容器
中进行修改(因为元素总是const的),但可以从容器中插入或删除。- 在内部,multiset中的元素总是按照其内部比较规则(类型比较)所指示的特定严格弱排序准则 进行排序。
- multiset容器通过key访问单个元素的速度通常比unordered_multiset容器慢,但当使用迭 代器遍历时会得到一个有序序列。
- multiset底层结构为二叉搜索树(红黑树)。
6、 multiset中的元素不能修改。
2、set与multiset区别
(1)set容器不能出现重复元素,multiset能出现重复元素。
(2)两者容器接口相同,count接口对于set来说不是1就是0,而对于multiset可以是实际元素个数,find接口对于set来说查找的元素是唯一的,而对于multiset来说查找的元素如果存在多个就返回红黑树中序遍历的第一个。
3、使用
//multiset
void test05()
{vector<int> v = { 1,1,3,4,5,2,4,4,4 };multiset<int> mset1(v.begin(), v.end());cout << "4的个数" << mset1.count(4)<<endl;multiset<int>::iterator mit = mset1.begin();//通过迭代器遍历while (mit != mset1.end()){cout << *mit << " ";mit++;}cout << endl;}
三、map
1、map介绍
C++中的map容器是一种关联容器,它存储的元素是一对键值(key-value)对(使用对组pair)。每个键值对映射一个特定的键到一个特定的值。在map中,任何两个键都不能相同,因为每个键只能映射到一个值。map中的元素总是按照键的顺序排序。
2、map特征
(1)自动排序:map中的元素会根据键自动排序。默认情况下,它使用<运算符来比较键,所以键类型需要支持这个运算符。
(2)唯一键:每个键在map中必须是唯一的。
(3)直接访问:可以通过键直接访问元素。
(4)map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。
3、接口使用
(1)模板参数
key: 键值对中key的类型
T: 键值对中value的类型
Compare:比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的 空间配置器
(1)常用构造、赋值接口
函数声明 | 功能 |
---|---|
map() | 空构造 |
map (const map& x); | 拷贝构造 |
map (InputIterator first, InputIterator last, const key_compare& comp = key_compare(), const allocator_type& = allocator_type()); | 用迭代区间构造 |
map& operator= (map&& x); | 赋值 |
使用:
//赋值,
//构造
void test06()
{//迭代区间构造vector<pair<int, int>> v = { {1,1},{2,2},{3,3} };map<int,int> mp1(v.begin(), v.end());//空构造map<int, int> mp2;//拷贝构造map<int, int> mp3(mp1);//赋值map<int, int> mp4;mp4 = mp1;
}
(2)常用迭代器接口
map迭代器是双向迭代器,只支持++或者–操作,不支持随机访问。
函数声明 | 功能 |
---|---|
begin() | begin:首元素的位置 |
end() | end最后一个元素的下一个位置 |
使用:
迭代器
void test07()
{//迭代区间构造vector<pair<int, int>> v = { {1,1},{2,2},{3,3} };map<int, int> mp1(v.begin(), v.end());//迭代器遍历map<int, int>::iterator it = mp1.begin();while (it != mp1.end()){cout << it->first << " ";it++;}cout << endl;
}
(3)常用插入、删除、修改、查找接口
函数声明 | 功能 |
---|---|
pair<iterator,bool> insert ( const value_type& x ) | 在map中插入键值对x,注意x是一个键值对,返回值也是键值对:iterator代表新插入元素的位置,bool代表释放插入成功 |
void erase ( iterator position ) | 删除position位置上的元素 |
size_type erase ( const key_type& x ) | 删除键值为x的元素,返回删除元素个数 |
void clear ( ) | 清空 |
iterator find ( const key_type& x ) | 在map中插入key为x的元素,找到返回该元素的位置的迭代器,否则返回end |
mapped_type& operator[] (const key_type& k); | 重载[],通过key返回value的引用,不用在key是相当于插入,value会调用其默认构造进行初始化 |
使用:
//插入等操作
void test08()
{map<int, int> mp1;//插入pair对象mp1.insert({ 1,1 });mp1.insert({ 2,2 });mp1.insert({ 3,3 });mp1.insert({ 4,4 });cout << "插入后:";map<int, int>::iterator it1 = mp1.begin();while (it1 != mp1.end()){cout << it1->first << " ";it1++;}cout << endl;//通过key删除mp1.erase(1);//通过迭代器删除map<int, int>::iterator it2 = mp1.begin();//mp1.erase(it2);cout << "删除后:";map<int, int>::iterator it3 = mp1.begin();while (it3 != mp1.end()){cout << it3->first << " ";it3++;}cout << endl;//查找map<int, int>::iterator it4 = mp1.find(3);if (it4 != mp1.end())cout << "存在" << endl;elsecout << "不存在" << endl;//通过[key]进行修改valuemp1[3] = 1000;//修改为1000cout << "修改后:" << mp1[3] << endl;
}
(4)常用大小相关的接口
函数声明 | 功能 |
---|---|
size_type count ( const key_type& x ) const | 返回key为x的键值在map中的个数,注意map中key是唯一的,因此该函数的返回值要么为0,要么为1,因此也可以用该函数来检测一个key是否在map中 |
size_type size() const; | 返回元素个数 |
bool empty() const; | 判断是否为空 |
使用:
void test09()
{//迭代区间构造vector<pair<int, int>> v = { {1,1},{2,2},{3,3} };map<int, int> mp1(v.begin(), v.end());cout << "大小:" << mp1.size() << endl;cout << "4的个数:" << mp1.count(4) << endl;if (!mp1.empty())cout << "不为空" << endl;elsecout << "为空" << endl;
}
四、multimap
1、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在底层用二叉搜索树(红黑树)来实现。
2、map与multimap的区别
(1)multimap和map的唯一不同就是:map中的key是唯一的,而multimap中key是可以重复的。
(2)接口multimap少了 at()、[ ]重载,count(key)接口对于map来说不是1就是0,对于multimap来说还可以是多个,find(key)接口对于map来说存在唯一值,对于multimap来说如果存在多个相同的key就会返回红黑树中序遍历的第一个,其他接口基本一样。
3、使用
void test10()
{//迭代区间构造vector<pair<int, int>> v = { {1,1},{2,2},{3,3},{2,2},{2,2} };multimap<int, int> mp1(v.begin(), v.end());cout << "2的个数:" << mp1.count(2) << endl;//迭代器遍历multimap<int, int>::iterator it = mp1.begin();while (it != mp1.end()){cout << it->first << " ";it++;}cout << endl;}