文章目录
🔥前言
set
容器的底层实现是二叉树,在插入元素的时候会自动进行升序的排序操作,set
容器有去重的功能,而multiset
容器允许插入相同元素…set
容器在STL
编程里常常用到,那么我就总结一下它的用法,抓住源码分析去重、排序的原理
1、set 容器基本操作,从构造到查找统计
1.1、set/ multiset 基本概念
特点:
- 所有元素都会在插入时自动被升序排序
本质:
- set/multiset属于关联式容器,底层结构是用二叉树实现
二者区别:
set
不允许容器中有重复的元素multiset
允许容器中有重复的元素
1.2、set构造和赋值
功能描述:
- 创建set容器以及赋值
函数原型:
set<T> st;
默认构造函数set(const set &st);
拷贝构造函数set& operator=(const set &st);
重载等号操作符
代码示例:
//遍历set集合
void printSet(set<int>& s) {
for (set<int>::iterator it = s.begin(); it != s.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
// 构造操作
void test1(){
// 默认构造
set<int> s1;
// 赋值操作
s1.insert(4);
s1.insert(2);
s1.insert(2);
s1.insert(1);
s1.insert(0);
// 拷贝构造
set<int> s2(s1);
// 重载赋值运算符
set<int> s3 = s2;
// 调用函数,遍历操作
printSet(s1);
printSet(s2);
printSet(s3);
// set 容器插入值使用inset方法
}
1.3、set 大小和交换
功能描述:
- 统计set容器大小以及交换set容器
函数原型:
size();
返回容器中元素的数目empty();
判断容器是否为空swap(st);
交换两个集合容器
代码示例:
// 大小操作
void test2() {
set<int> s1;
s1.insert(1);
s1.insert(0);
s1.insert(2);
s1.insert(4);
if (s1.empty()) {
cout << "set 为空" << endl;
}else {
cout << "set 不为空" << endl;
cout << "输出集合内元素:";
printSet(s1);
cout << "大小为:" << s1.size();
}
}
// 交换操作
void test3() {
set<int> s1,s2;
s1.insert(1);
s1.insert(0);
s1.insert(2);
s1.insert(4);
s2.insert(10);
s2.insert(20);
s2.insert(40);
s2.insert(0);
cout << "交换前:" << endl;
printSet(s1);
printSet(s2);
cout << "交换后:" << endl;
s1.swap(s2);
printSet(s1);
printSet(s2);
}
1.4、set插入和删除
功能描述:
- set容器进行插入数据和删除数据
函数原型:
insert(elem);
在容器中插入元素。clear();
清除所有元素erase(pos);
删除pos迭代器所指的元素,返回下一个元素的迭代器。erase(beg, end);
删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。erase(elem);
删除容器中值为elem的元素。
代码示例:
// 插入删除操作
void test4() {
set<int> s1,s2;
s1.insert(1);
s1.insert(0);
s1.insert(2);
s1.insert(20);
s1.insert(24);
s1.insert(4);
s2 = s1;
// 遍历
printSet(s1);
// 按照迭代器删除
s1.erase(s1.begin());
s1.erase(--s1.end());
printSet(s1);
// 按照值删除
s1.erase(4);
printSet(s1);
// 清空set ,两种方法
s1.erase(s1.begin(), s1.end());
printSet(s1);
s2.clear();
}
1.5、set查找和统计
功能描述:
- 对set容器进行查找数据以及统计数据
函数原型:
find(key);
查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
count(key);
统计key的元素个数
代码示例:
// 查找和统计操作
void test5() {
set<int> s1;
s1.insert(1);
s1.insert(0);
s1.insert(2);
s1.insert(4);
//auto pos = s1.find(2);
auto pos = s1.find(3);
if (pos != s1.end()) {
cout << "找到元素,值为:" << *pos << endl;
}
else {
cout << "元素未找到" << endl;
}
// 统计操作,对于set 而言要么是零要么是一
int ans = s1.count(1);
int ansl = s1.count(3);
cout << ans << " " << ansl;
}
auto
关键字可用于数据类型的自动推导,可以替换某些情况下迭代器的写法,比较方便
2、set 和 multiset 插入数据特点不同的原因
上面提到二者的不同点在于是否可以插入不同的数据,那么就来看看二者insert
插入方法的源码
2.1、set 中的insert 源码分析
查看set 中的insert 源码:
- 可以看到这里的
insert
返回值类型是一个pair
二元组,包含迭代器类型和布尔类型 - 那么再进行插入操作的时候,如果检查到容器中已经有相同元素,就会返回
false
,不进行插入 - 有关二元组的创建将在下面解释
2.2、pair 对组的创建及使用
功能描述:
- 成对出现的数据,利用对组可以返回两个数据
两种创建方式:
pair<type, type> p ( value1, value2 );
pair<type, type> p = make_pair( value1, value2 );
代码示例:
// pair 对组的使用 make_pair()
void testPair() {
// 默认构造
pair<string, int> pre("微凉", 10);
pair<string, int> ptr = make_pair("秋意", 24);
// 获取值
cout << pre.first << ptr.first << "\n" << pre.second << ptr.second << endl;
}
- 推荐使用
make_pair
的方式创建二元组,这样看起来比较清晰 - 二元组通过调用
first
和second
来访问对应的属性值
2.3、multiset 中的insert 源码分析
查看multiset 中的insert 源码:
multiset
中的insert 返回值类型是一个迭代器,并没有布尔类型- 所以可以插入重复数据
3、内置与自定义数据类型的排序规则
set
容器插入时默认自动升序排序,那么怎么修改排序规则呢:- 使用仿函数来指定排序规则
3.1、内置数据类型的排序
写一个类,重载类内的函数运算符()
:
// 修改 set 容器的排序规则
class Cmp {
public:
bool operator()(int v1,int v2) const
{
return v1 > v2;
}
};
void test7() {
// 默认是升序
set<int> s1;
s1.insert(1);
s1.insert(0);
s1.insert(2);
s1.insert(4);
printSet(s1);
// 修改为降序,借助仿函数
set<int, Cmp> s2;
s2.insert(1);
s2.insert(0);
s2.insert(2);
s2.insert(4);
for (auto it = s2.begin(); it != s2.end(); it++) {
cout << *it << " ";
}
}
- 要注意的是重载的函数需要是常函数,要加上
const
关键字- 重载的细节可以参考我这篇博客:详解重载函数调用运算符
- 创建
set
容器的时候就要指定排序规则,在尖括号中这样表示:set<int, Cmp>
3.2、自定义数据类型的排序
set
容器插入自定义数据的时候如果不指定排序规则,默认的升序排序也无法进行,因此需要事先指定排序规则:
// 定义Person 类
class Person {
int age;
string name;
public:
Person(string name, int age) {
this->name = name;
this->age = age;
}
int getAge() const {
return this->age;
}
string getName() const {
return this->name;
}
};
// 自定义排序规则
class Compare {
public:
bool operator()(const Person &p1, const Person &p2)const {
return p1.getAge() > p2.getAge();
}
};
void test8() {
set<Person, Compare> s;
Person p1("叶落", 20);
Person p2("微凉", 24);
Person p3("秋意", 22);
Person p4("秋白", 18);
s.insert(p1);
s.insert(p2);
s.insert(p3);
s.insert(p4);
for (auto it = s.begin(); it != s.end(); it++) {
cout << "姓名:" << it->getName() << "\t年龄:" << it->getAge()<<endl;
}
}
运行效果:
- 自定义数据类型重载时的要求比较严格:
- 不仅函数需要加
const
限制,参数列表中也都要加const
- 不仅函数需要加
- 由于
Person
类中的属性采用了封装,那么在对应的get
方法中也要是常函数
希望以此文章帮助大家快速学会、复习set
容器的使用,创作不易,希望大家能够点赞支持!