STL 容器:set
目录
1 set 的概念
set 是 STL 容器的一种,它的底层实现是红黑树(特殊的二叉搜索树),因此,它的增删查效率为 O(log2N),并且 set 还分为了 普通的 set 和 multiset,普通的 set 中不会出现重复的值,multiset 中则会出现重复的值,因此,它们的部分接口使用起来也会有不一样的效果
如果要使用 set,就需要包含头文件 set,由于容器处于 std 命名空间下,所以需要指定命名空间 std
#include <set>
2 set 的构造方式

在构造 set 或 multiset 时,要使用以下语法:
set<数据类型,比较规则> 对象名();
multiset<数据类型,比较规则> 对象名();
在这其中,数据类型和对象名必须要给定,但是比较规则不是必须的,如果没有指定比较规则,那么默认使用升序,也就是按照二叉搜索树左小于根小于右的规则来存放数据
构造 set 时,有三种选择:
(1)构造空的 set 或 multiset
对象名后不加括号,就是在构造空的 set
int main()
{
set<int> s; //空set
multiset<int> muls;
return 0;
}
(2)通过另外一个容器的内容来构造 set 或 multiset
构造时,需要给出另一个容器某个范围内的数据
int main()
{
vector<int> v({ 5,3,1,2,4 });
set<int> s(v.begin(), v.end()); //用v内的所有数据构造s
multiset<int> muls(v.begin(), v.end());
return 0;
}
(3)通过另一个 set 或 multiset 来构造 set 或 multiset(拷贝构造)
int main()
{
vector<int> v({ 5,3,1,2,4 });
set<int> s(v.begin(), v.end());
set<int> s2(s); //拷贝构造
multiset<int> muls(v.begin(), v.end());
multiset<int> muls2(muls);
return 0;
}
3 set 的常用接口
3.1 set 的遍历
由于 set 底层是使用红黑树来实现的,所以它的迭代器是双向迭代器,只能++,- -,不能随机访问,因此它也不支持使用下标遍历,只支持迭代器和范围 for 遍历
(1) 迭代器遍历
使用迭代器遍历 set 时,要用到以下接口
| 接口名称 | 作用 | 返回值类型 |
|---|---|---|
| begin() | 返回指向第一个元素的迭代器 | 普通对象返回 iterator,const 对象返回 const_iterator |
| end() | 返回指向最后一个元素的下一个位置的迭代器 | 普通对象返回 iterator,const 对象返回 const_iterator |
| rbegin() | 返回指向最后一个元素的迭代器 | 普通对象返回 reverse_iterator,const 对象返回 const_reverse_iterator |
| rend() | 返回指向第一个元素的前一个位置的迭代器 | 普通对象返回 reverse_iterator,const 对象返回 const_reverse_iterator |
正向遍历:
int main()
{
//遍历set
set<int> s({ 5,3,1,2,4 });
set<int>::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
it++;
}
cout << endl;
//遍历multiset
multiset<int> muls({ 8,7,4,5,9 });
set<int>::iterator it2 = muls.begin();
while (it2 != muls.end())
{
cout << *it2 << " ";
it2++;
}
return 0;
}
结果:
1 2 3 4 5
4 5 7 8 9
反向遍历:
int main()
{
//遍历set
set<int> s({ 5,3,1,2,4 });
set<int>::reverse_iterator rit = s.rbegin();
while (rit != s.rend())
{
cout << *rit << " ";
rit++;
}
cout << endl;
//遍历multiset
multiset<int> muls({ 8,7,4,5,9 });
set<int>::reverse_iterator rit2 = muls.rbegin();
while (rit2 != muls.rend())
{
cout << *rit2 << " ";
rit2++;
}
return 0;
}
结果:
5 4 3 2 1
9 8 7 5 4
(2) 范围 for 遍历
int main()
{
//遍历set
set<int> s({ 5,3,1,2,4 });
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
//遍历multiset
multiset<int> muls({ 8,7,4,5,9 });
for (auto e : muls)
{
cout << e << " ";
}
return 0;
}
结果:
1 2 3 4 5
4 5 7 8 9
3.2 set 中与容量相关的接口
3.2.1 empty
empty 的作用是判断 set 是否为空,是空返回 true,不是空则返回 false
int main()
{
set<int> s1;
set<int> s2({ 5,3,1,2,4 });
cout << s1.empty() << endl;
cout << s2.empty() << endl;
multiset<int> muls1;
multiset<int> muls2({ 8,7,4,5,9 });
cout << muls1.empty() << endl;
cout << muls2.empty() << endl;
return 0;
}
结果:
1
0
1
0
3.2.2 size
size 的作用是获取 set 中有效元素的个数
int main()
{
set<int> s1({ 5,3,1,2,4 });
cout << s1.size() << endl;
multiset<int> muls1({ 8,7,4,5,9 });
cout << muls1.size() << endl;
return 0;
}
结果:
5
5
3.2.3 max_size
max_size 的作用是获取 set 能存放的数据个数
int main()
{
set<int> s1({ 5,3,1,2,4 });
cout << s1.max_size() << endl;
multiset<int> muls1({ 8,7,4,5,9 });
cout << muls1.max_size() << endl;
return 0;
}
结果:
576460752303423487
576460752303423487
3.3 set 的增删查
由于 set 是由红黑树实现的,并且存的只有一个值,不是一个键值对,所以 set 不存在修改操作,如果对 set 中的值进行修改,那么 set 内部的结构就会混乱,就不会是一棵红黑树 (或BST)
3.3.1 insert

insert 的作用是往 set 集合中插入一个元素,在插入时,会根据给定的比较规则来查找合适的插入位置,插入的方式有三种:
(1)直接插入指定的值
使用这种方式插入 val 时,最终会返回一个键值对,这个键值对内保存了一个迭代器和一个布尔类型的值,它会因为 set 和 multiset 有所不同
普通的 set 中没有重复的值,所以如果原来 set 中就有待插入的值 val,那么返回的迭代器就会指向它,布尔类型的值为 false,如果原来 set 中没有待插入的值 val,那么返回的迭代器也会指向它,布尔类型的值为 true
multiset 中可以有重复的值,所以返回的迭代器会指向 val,布尔类型的值永远为 false
int main()
{
set<int> s1({ 5,3,1,2,4 });
//插入不存在的值
pair<set<int>::iterator, bool> p = s1.insert(6);
cout << *(p.first) << " " << p.second << endl;
//插入已存在的值
pair<set<int>::iterator, bool> p2 = s1.insert(5);
cout << *(p2.first) << " " << p2.second << endl;
cout << endl;
multiset<int> muls1({ 8,7,4,5,9 });
//插入不存在的值
pair<set<int>::iterator, bool> p3 = s1.insert(6);
cout << *(p3.first) << " " << p3.second << endl;
//插入已存在的值
pair<set<int>::iterator, bool> p4 = s1.insert(5);
cout << *(p4.first) << " " << p4.second << endl;
return 0;
}
结果:
6 1
5 0
6 0
5 0
(2)在指定的位置插入指定的值
以这种方式插入值 val 时,会返回指向它的迭代器,虽然是在指定位置插入值,但是插入时仍然会遵守二叉搜索树的规则先查找再插入,保证有序
int main()
{
set<int> s1({ 5,3,1,2,4 });
set<int>::iterator it1 = s1.insert(s1.begin(), 6);
cout << *it1 << endl;
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
multiset<int> muls1({ 8,7,4,5,9 });
set<int>::iterator it2 = muls1.insert(muls1.begin(), 6);
cout << *it2 << endl;
for (auto e : muls1)
{
cout << e << " ";
}
return 0;
}
结果:
6
1 2 3 4 5 6
6
4 5 6 7 8 9
(3)插入指定区间内的值
int main()
{
vector<int> v({ 5,3,1,2,4 });
set<int> s1;
s1.insert(v.begin(), v.end());
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
multiset<int> muls1;
muls1.insert(v.begin() + 1, v.end() - 1);
for (auto e : muls1)
{
cout << e << " ";
}
return 0;
}
结果:
1 2 3 4 5
1 2 3
3.3.2 erase

erase 的作用是删除 set 中的一个指定元素,删除的方式有三种:
(1)删除指定位置上的值
int main()
{
set<int> s1({ 1,2,3,4,5 });
s1.erase(s1.begin()); //删除1
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
multiset<int> muls1({ 4,4,5,5,6,6 });
muls1.erase(muls1.begin()); //删除4
for (auto e : muls1)
{
cout << e << " ";
}
return 0;
}
结果:
2 3 4 5
5 7 8 9
(2)删除指定的值
用这种方式删除普通 set 中的数据时,只会返回 0 或 1,因为普通 set 中数据不会重复,要么有 1 个要么就是没有
但是删除 multiset 中的数据时,会将所有相等的数据删除,返回删除的数据个数,因为 multiset 中允许数据重复
int main()
{
set<int> s1({ 1,2,3,4,5 });
cout << s1.erase(1) << endl; //删除1
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
multiset<int> muls1({ 4,4,5,5,6,6 });
cout << muls1.erase(4) << endl; //删除4
for (auto e : muls1)
{
cout << e << " ";
}
return 0;
}
结果:
1
2 3 4 5
2
5 5 6 6
(3)删除指定区间上的值
int main()
{
set<int> s1({ 1,2,3,4,5 });
s1.erase(++s1.begin(), --s1.end()); //删除2~4
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
multiset<int> muls1({ 4,4,5,5,6,6 });
muls1.erase(++muls1.begin(), --muls1.end()); //删除第二个4~第一个6
for (auto e : muls1)
{
cout << e << " ";
}
return 0;
}
结果:
1 5
4 6
3.3.3 find
find 的作用是在 set 中查找指定的值,返回它的迭代器
int main()
{
set<int> s1({ 1,2,3,4,5 });
set<int>::iterator it = s1.find(2); //查找2
cout << *it << endl;
cout << endl;
multiset<int> muls1({ 4,4,5,5,6,6 });
multiset<int>::iterator it2 = muls1.find(5); //查找5
cout << *it2 << endl;
return 0;
}
结果:
2
5
3.3.4 swap
swap 的作用是交换两个 set 中的值
int main()
{
set<int> s1({ 1,2,3,4,5 });
set<int> s2({ 6,7,8,9,10 });
cout << "s1 before swap: " << endl;
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
cout << "s2 before swap: " << endl;
for (auto e : s2)
{
cout << e << " ";
}
s1.swap(s2); //交换s1,s2
cout << endl;
cout << "s1 after swap: " << endl;
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
cout << "s2 after swap: " << endl;
for (auto e : s2)
{
cout << e << " ";
}
cout << endl;
cout << endl;
multiset<int> muls1({ 4,4,5,5,6,6 });
multiset<int> muls2({ 7,7,8,8,9,9 });
cout << "muls1 before swap: " << endl;
for (auto e : muls1)
{
cout << e << " ";
}
cout << endl;
cout << "muls2 before swap: " << endl;
for (auto e : muls2)
{
cout << e << " ";
}
muls1.swap(muls2); //交换muls1,muls2
cout << endl;
cout << "muls1 after swap: " << endl;
for (auto e : muls1)
{
cout << e << " ";
}
cout << endl;
cout << "muls2 after swap: " << endl;
for (auto e : muls2)
{
cout << e << " ";
}
return 0;
}
结果:
s1 before swap:
1 2 3 4 5
s2 before swap:
6 7 8 9 10
s1 after swap:
6 7 8 9 10
s2 after swap:
1 2 3 4 5
muls1 before swap:
4 4 5 5 6 6
muls2 before swap:
7 7 8 8 9 9
muls1 after swap:
7 7 8 8 9 9
muls2 after swap:
4 4 5 5 6 6
3.3.5 clear
clear 的作用是将 set 清空
int main()
{
set<int> s1({ 1,2,3,4,5 });
s1.clear();
cout << s1.empty() << endl;
cout << endl;
multiset<int> muls1({ 4,4,5,5,6,6 });
muls1.clear();
cout << muls1.empty() << endl;
return 0;
}
结果:
1
1
3.4 set 的其它操作
3.4.1 lower_bound 和 upper_bound
lower_bound的作用是查找出一个指定的值,返回它的迭代器,如果有多个相同的,则返回中序的第一个
upper_bound的作用是查找出大于指定值的第一个值,返回它的迭代器
一般它们会结合使用,来确定左闭右开的区间
int main()
{
set<int> s1({ 10,20,30,40,50,60 });
set<int>::iterator lowerIt = s1.lower_bound(20); //查找20
cout << *lowerIt << endl;
set<int>::iterator upperIt = s1.upper_bound(40); //查找大于40的第一个数
cout << *upperIt << endl;
set<int> s2(lowerIt, upperIt);
//[20,30,40,50)
for (auto e : s2)
{
cout << e << " ";
}
cout << endl;
cout << endl;
multiset<int> muls1({ 10,10,20,20,30,30,40,40 });
multiset<int>::iterator lowerIt2 = muls1.lower_bound(20); //查找第一个出现的20
cout << *lowerIt2 << endl;
multiset<int>::iterator upperIt2 = muls1.upper_bound(30); //查找大于30的第一个数
cout << *upperIt2 << endl;
multiset<int> muls2(lowerIt2, upperIt2);
//[20,20,30,30,40)
for (auto e : muls2)
{
cout << e << " ";
}
return 0;
}
结果:
20
50
20 30 40
20
40
20 20 30 30
3.4.2 count
count 的作用是计算 set 中指定值的个数,对于普通的 set 来说,只会返回 0 或 1,因为值不会重复,要么有一个值要么没有值,对于 multiset 来说,则有可能会返回各个正整数,因为 multiset 中值会重复
int main()
{
set<int> s1({ 1,2,3,4,5 });
cout << s1.count(2) << endl;
cout << s1.count(6) << endl;
cout << endl;
multiset<int> muls1({ 4,4,5,5,6,6 });
cout << muls1.count(4) << endl;
cout << muls1.count(7) << endl;
return 0;
}
结果:
1
0
2
0
3.4.3 equal_range
equal_range 的作用是返回相等的值的区间,该区间是左闭右开的,返回值是一个键值对,相当于用两个迭代器确定了一个区间。这个接口对于 set 来说意义不大,因为 set 中值不会重复,但是对于 multiset 来说是有意义的,因为 multiset 的值会重复
int main()
{
multiset<int> muls1({ 4,4,4,4,7,9,10,11 });
pair<multiset<int>::iterator, multiset<int>::iterator> p = muls1.equal_range(4);
multiset<int> muls2(p.first, p.second); //使用区间构造multiset
for (auto e : muls2)
{
cout << e << " ";
}
return 0;
}
结果:
4 4 4 4
更多推荐
所有评论(0)