【C++/STL】set和multiset的使用
目录
前言
一、序列式容器和关联式容器
二、键值对
三、set
set的介绍
构造函数
insert
find
erase
swap
empty
size()
count()
upper_bound
upper_bound
四、multiset
insert
find/erase
count
前言
set 是 STL 中的容器之一,不同于普通容器,它的查找速度极快,元素自动排序且不重复,常用来存储各种经常被检索的数据,容器的底层是平衡二叉搜索树中的红黑树,在解决许多实际问题时发挥着不可替代的作用。
一、序列式容器和关联式容器
在以往学习的
string、vector、list等STL容器都是序列式容器,序列式容器的特点就是底层为线性序列的数据结构,它们的元素之间通常没有相关性,其元素按照逻辑顺序进行存储和访问。
而我们学习的set和map属于另一种关联式容器,其中存储的是〈key, value〉的键值对,这就意味着可以按照键值大小 key 以某种特定的规则放置于适当的位置,查找速度相对更快。
二、键值对
键值对是一种用来表示具有一一对应关系的结构,该结构中一般只包含两个成员变量:key 和 value,前者表示键值,后者表示实值。

template<class T1,class T2>
struct pair
{typedef T1 first_type;typedef T2 second_type;//成员变量first_type first;second_type second;//构造pair():first(first_type()),second(second_type()){}
};
- pair 中的 first 表示键值,second 则表示实值,在给 关联式容器 中插入数据时,可以构建 pair 对象
//可以匿名实现
pair<string, int>("hello", 123);//也可以使用make_pair实现
make_pair("hello", 123); //构建出的匿名对象与上面的一致
三、set
set的介绍
- set是按照一定次序存储元素的容器,使用set的迭代器遍历set中的元素,可得到有序序列。
- set当中存储元素的key和value都是唯一且相同的,不可以重复。(因此可以使用 set 去重)
- set中的元素不能在容器中修改 (元素总是const) ,但是可以从容器中插入或删除它们。
- 在内部,set中的元素总是按照其内部比较对象所指示的特定严格弱排序准则进行排序。当不传入内部比较对象时,set中的元素默认按照小于来比较。
注意:
- set 中 插入元素时,只需要插入 value 即可,不需要构造键值对,如insert(3)。
- set 中序遍历得到的结果就是一个有序序列,默认是升序序列,因为底层是二叉平衡搜索树(红黑树)
- set当中查找某个元素的时间复杂度为logN。

构造函数

主要有几种方式:
(1) 无参构造空容器
set<int> s1; // 构造一个int类型的空容器
(2) 拷贝构造某类型容器
set<int> s2(s1); // 拷贝构造int类型s1容器的复制品
(3) 使用迭代器区间进行初始化构造
vector<int> v1 = { 1,2,3,4,5 };
set<int> s3(v1.begin(), v1.end()); // 构造vector对象某段区间的复制品
insert
在 set 中插入元素 val ,实际插入的是 <val,val> 构成的键值对
- 如果插入成功,返回 <val在set中的位置, true>
- 如果插入失败,说明 val 在set中已经存在,返回 <val在set中的位置, false>

void testset()
{set<int> s;// 插入元素s.insert(4);s.insert(5);s.insert(3);s.insert(1);s.insert(2);s.insert(6);s.insert(3);s.insert(3);// 遍历for (auto e : s){cout << e << " ";}
}

find
在容器中搜索查找 val 元素,如果找到,则返回一个迭代器,否则返回 set::end 的迭代器

erase

删除set方式可以采用以下三种:

swap
交换两set容器内容
#include <iostream>
#include <set>
using namespace std;template<class T>
void print(const T& con)
{for (const auto& e : con){cout << e << ' ';}cout << endl;
}int main()
{set<int> s1 = { 1,2,3,4 };set<int> s2 = { 4,5,6 };cout << endl << "交换前:" << endl;cout << "s1: ";print(s1);cout << "s2: ";print(s2);s1.swap(s2);//交换cout << endl << "交换后:" << endl;cout << "s1: ";print(s1);cout << "s2: ";print(s2);return 0;
}

empty
判断 set 容器是否为空,空返回 ture,非空返回 false

size()
返回 set 中的有效元素个数

count()

count 返回容器中值为 val 的元素个数。
set 容器中的所有元素都是唯一的,所以该函数只能返回 1 或 0,因此可以判断一个元素是否在 set 容器中。但对于允许出现重复元素的 multiset,它还可以用于统计某元素的个数。
upper_bound
返回指向第一个不小于当前元素的迭代器。
set<int> s=({1,2,3,4,7,8,9});// 如果3存在就返回大于3位置的迭代器
auto lowIt = s.upper_bound(3);
cout << *lowIt << endl;// 如果6不存在就返回比6大的位置的迭代器
lowIt = s.upper_bound(6);
cout << *lowIt << endl;输出结果是:
3
7
upper_bound
返回指向第一个大于当前元素的迭代器。
set<int> s=({1,2,3,4,7,8,9});// 如果3存在就返回大于3位置的迭代器
auto lowIt = s.upper_bound(3);
cout << *lowIt << endl;// 如果6不存在就返回比6大的位置的迭代器
lowIt = s.upper_bound(6);
cout << *lowIt << endl;输出结果是:
4
7
四、multiset
set 和 multiset 的核心区别是:set 不允许容器内有重复元素,能够达到去重的效果;而 multiset 支持插入重复元素。
set 和 multiset 的接口功能几乎相同,下面讲一下使用略微区别的地方。
insert

支持插入重复值,返回值是新插入位置的迭代器。
find/erase

查找和删除它找到的位置是第一个,搜索树的迭代器本身是利用中序去实现的,所以多个key的情况,返回的是中序的第一个key
count
multiset中的count就有了意义,可以看到重复元素的个数了。

