set 容器定义于<set>头文件,并位于 std 命名空间中。如果想在程序中使用 set 容器,该程序代码应先包含如下语句:
#include <set>
using namespace std;
set 容器的类模板定义如下:
template <
class T, // 键 key 和值 value 的类型
class Compare = less<T>, // 指定 set 容器内部的排序规则
class Alloc = allocator<T> // 指定分配器对象的类型
> class set;
注意,由于 set 容器存储的各个键值对,其键和值完全相同,也就意味着它们的类型相同,因此 set 容器类模板的定义中,仅有第 1 个参数用于设定存储数据的类型。
对于 set 类模板中的 3 个参数,后 2 个参数自带默认值,且几乎所有场景中只需使用前 2 个参数,第 3 个参数不会用到。
🐢set容器
和 map、multimap 容器不同,使用 set 容器存储的各个键值对,要求键 key 和值 value 必须相等。
举个例子,如下有 2 组键值对数据:
{<'a', 1>, <'b', 2>, <'c', 3>} {<'a', 'a'>, <'b', 'b'>, <'c', 'c'>}显然,第一组数据中各键值对的键和值不相等,而第二组中各键值对的键和值对应相等。对于 set 容器来说,只能存储第 2 组键值对,而无法存储第一组键值对。
基于 set 容器的这种特性,当使用 set 容器存储键值对时,只需要为其提供各键值对中的 value 值(也就是 key 的值)即可。仍以存储上面第 2 组键值对为例,只需要为 set 容器提供 {‘a’,‘b’,‘c’} ,该容器即可成功将它们存储起来。
🐢set的简介
-
set是一个集合容器,其中所包含的元素是唯一的,集合中的元素按一定的顺序排列。元素插入过程是按排序规则插入,所以不能指定插入位置。
-
set采用{% post_link 程序员内功/算法与数据结构/红黑树 %}变体的数据结构实现,红黑树属于平衡二叉树。在插入操作和删除操作上比vector快。
-
set不可以直接存取元素。(不可以使用at.(pos)与[]操作符)。
-
multiset与set的区别:set支持唯一键值,每个元素值只能出现一次;而multiset中同一值可以出现多次。
- 不可以直接修改set或multiset容器中的元素值,因为该类容器是自动排序的。如果希望修改一个元素值,必须先删除原有的元素,再插入新的元素。
🐢创建C++ set容器的几种方法
常见的创建set容器的方法,大致有以下5种:
1)默认构造函数
调用默认构造函数,创建空的set容器。set对象的默认构造
set<int> setInt; //一个存放int的set容器。
set<float> setFloat; //一个存放float的set容器。
set<string> setString; //一个存放string的set容器。
由此就创建好了一个 set 容器,该容器采用默认的std::less<T>规则,会对存储的 string 类型元素做升序排序。注意,由于 set 容器支持随时向内部添加新的元素,因此创建空 set 容器的方法是经常使用的。
2)创建并初始化
set<string> myBlogSet{"http://fole-del.github.io",
"http://fole-del.gitee.io",
"http://www.mingsrc.xyz"};
由此即创建好了包含 3 个 string 元素的 myBlogSet容器。由于其采用默认的 std::less 规则,因此其内部存储 string 元素的顺序如下所示:
http://fole-del.gitee.io
http://fole-del.github.io
http://www.mingsrc.xyz
3)拷贝构造函数
使用set类模板提供的拷贝构造函数,在第2种myBlog的基础上,执行如下代码:
std::set<std::string> copyBlogSet(myBlogSet);
//等同于
//std::set<std::string> copyBlogSet = myBlogSet
该行代码在创建 copyBlogSet 容器的基础上,还会将myBlogSet容器中存储的所有元素,全部复制给copyBlogSet容器一份。
除此之外,C++11标准还为set类模板新增了移动构造函数,其功能是实现创建新 set 容器的同时,利用临时的 set 容器为其初始化。比如:
set<string> retBlogSet() {
std::set<std::string> myBlogSet{ "http://fole-del.github.io",
"http://fole-del.gitee.io",
"http://www.mingsrc.xyz"};
return myBlogSet;
}
std::set<std::string> copyBlogSet(retBlogSet());
//或者
//std::set<std::string> copyBlogSet = retBlogSet();
4) 使用已有set的部分初始化set
在第 3 种方式的基础上,set 类模板还支持取已有 set 容器中的部分元素,来初始化新 set 容器。例如:
set<string> myBlogSet{"http://fole-del.github.io",
"http://fole-del.gitee.io",
"http://www.mingsrc.xyz"};
std::set<std::string> copyBlogSet(++myBlogSet.begin(), myBlogSet.end());
由此初始化的copyset容器,其内部有如下2两个字符串:
http://www.mingsrc.xyz
5)修改set的排序规则
以上几种方式创建的 set 容器,都采用了默认的std::less<T>规则。其实,借助 set 类模板定义中第 2 个参数,我们完全可以手动修改 set 容器中的排序规则。比如:
set<string,greater<string> > myBlogSet{"http://fole-del.github.io",
"http://fole-del.gitee.io",
"http://www.mingsrc.xyz"};
http://fole-del.github.io
http://fole-del.gitee.io
C++ STL set容器包含的成员方法
| 成员方法 | 功能 |
|---|---|
| begin() | 返回指向容器中第一个(注意,是已排好序的第一个)元素的双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
| end() | 返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
| rbegin() | 返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。 |
| rend() | 返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。 |
| cbegin() | 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。 |
| cend() | 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。 |
| crbegin() | 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。 |
| crend() | 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。 |
| find(val) | 在 set 容器中查找值为 val 的元素,如果成功找到,则返回指向该元素的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
| lower_bound(val) | 返回一个指向当前 set 容器中第一个大于或等于 val 的元素的双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
| upper_bound(val) | 返回一个指向当前 set 容器中第一个大于 val 的元素的迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
| equal_range(val) | 该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的值为 val 的元素(set 容器中各个元素是唯一的,因此该范围最多包含一个元素)。 |
| empty() | 若容器为空,则返回 true;否则 false。 |
| size() | 返回当前 set 容器中存有元素的个数。 |
| max_size() | 返回 set 容器所能容纳元素的最大个数,不同的操作系统,其返回值亦不相同。 |
| insert() | 向 set 容器中插入元素。 |
| erase() | 删除 set 容器中存储的元素。 |
| swap() | 交换 2 个 set 容器中存储的所有元素。这意味着,操作的 2 个 set 容器的类型必须相同。 |
| clear() | 清空 set 容器中所有的元素,即令 set 容器的 size() 为 0。 |
| emplace() | 在当前 set 容器中的指定位置直接构造新元素。其效果和 insert() 一样,但效率更高。 |
| emplace_hint() | 在本质上和 emplace() 在 set 容器中构造新元素的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示新元素生成位置的迭代器,并作为该方法的第一个参数。 |
| count(val) | 在当前 set 容器中,查找值为 val 的元素的个数,并返回。注意,由于 set 容器中各元素的值是唯一的,因此该函数的返回值最大为 1。 |
下面程序演示了表 1 中部分成员函数的用法:
#include <iostream>
#include <set>
#include <string>
using namespace std;
int main(){
//创建空set容器
std::set<std::string> myBlogSet;
//空set容器不存储任何元素
cout << "1、myBlogSet size = " << myset.size() << endl;
//向myBlogSet容器中插入新元素
myset.insert("http://www.mingsrc.xyz");
myset.insert("http://fole-del.github.io");
myset.insert("http://fole-del.gitee.io");
cout << "2、myBlogSet size = " << myBlogSet.size() << endl;
//利用双向迭代器,遍历myBlogSet
for (auto iter = myBlogSet.begin(); iter != myBlogSet.end(); ++iter) {
cout << *iter << endl;
}
return 0;
}
程序的执行结果为:
2、myset size = 3
http://fole-del.gitee.io
http://fole-del.github.io
http://www.mingsrc.xyz
🐢set的插入、排序、删除、查找操作
set的插入
set.insert(elem); //在容器中插入元素。
Set集合的元素排序
set<int,less > setIntA; //该容器是按升序方式排列元素。
set<int,greater> setIntB; //该容器是按降序方式排列元素。
set 相当于 set<int,less>。
less与greater中的int可以改成其它类型,该类型主要要跟set容纳的数据类型一致。
注意点1: less<>与greate< >是什么?
注意点2:如果set<>不包含int类型,而是包含自定义类型,set容器如何排序?
要解决如上两个问题,需要了解容器的函数对象,也叫伪函数,英文名叫functor。
下面将讲解什么是functor以及用法。
使用stl提供的函数对象
set<int,greater> setIntB;
setIntB.insert(3);
setIntB.insert(1);
setIntB.insert(5);
setIntB.insert(2);
此时容器setIntB就包含了按顺序的5,3,2,1元素
函数对象functor的用法
尽管函数指针被广泛用于实现函数回调,但C++还提供了一个重要的实现回调函数的方法,那就是函数对象。
functor,翻译成函数对象,伪函数,算符,是重载了“()”操作符的普通类对象。从语法上讲,它与普通函数行为类似。
greater<>与less<>就是函数对象。
下面举出greater的简易实现原理。
class greater
{
public:
bool operator()(const int& iLeft, const int& iRight)
{
return iLeft > iRight; //如果是实现less<int>的话,这边是写return (iLeft<iRight);
}
};
容器就是调用函数对象的operator()方法去比较两个值的大小。
仿函数练习
学生包含学号,姓名属性,现要求任意插入几个学生对象到set容器中,使得容器中的学生按学号的升序排序。
解:
#include<iostream>
#include<set>
using namespace std;
//学生类
class CStudent
{
public:
CStudent(int iID, string strName)
{
m_iID = iID;
m_strName = strName;
}
//private:
int m_iID; //学号
string m_strName; //姓名
};
//为保持主题鲜明,本类不写拷贝构造函数,不类也不需要写拷贝构造函数。但大家仍要有考虑拷贝构造函数的习惯。
//函数对象
struct StuFunctor
{
bool operator()(const CStudent& stu1,const CStudent& stu2) const
{
return (stu1.m_iID < stu2.m_iID);
}
};
int main()
{
set<CStudent, StuFunctor> setStu;
setStu.insert(CStudent(3, "小张"));
setStu.insert(CStudent(1, "小李"));
setStu.insert(CStudent(5, "小王"));
setStu.insert(CStudent(2, "小刘"));
for (auto i : setStu)
{
cout << i.m_iID << " ";
}
return 0;
}
set对象的拷贝构造与赋值
set(const set &st); //拷贝构造函数
set& operator=(const set &st); //重载等号操作符
set.swap(st); //交换两个集合容器
set的大小
set.size(); //返回容器中元素的数目
set.empty();//判断容器是否为空
set的删除
set.clear(); //清除所有元素
set.erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器。
set.erase(beg,end); //删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
set.erase(elem); //删除容器中值为elem的元素。
set的查找
set.find(elem); //查找elem元素,返回指向elem元素的迭代器。
set.count(elem); //返回容器中值为elem的元素个数。对set来说,要么是0,要么是1。对multiset来说,值可能大于1。
set.lower_bound(elem); //返回第一个>=elem元素的迭代器。
set.upper_bound(elem); // 返回第一个>elem元素的迭代器。
set.equal_range(elem); //返回一对迭代器,这两个迭代器分别用于发现set中其键大于指定键的第一个元素,以及集中其键等于或大于指定键的第一个元素。
以上函数返回两个迭代器,而这两个迭代器被封装在pair中。
本文详细介绍了C++标准库中的set容器,包括其定义、特点、创建方法及成员方法等。探讨了set容器的插入、排序、删除和查找操作,并通过实例展示了如何使用自定义类型进行排序。
3672

被折叠的 条评论
为什么被折叠?



