1. 简介
1.1 数学中的集合
No. | 特性 | 说明 |
---|---|---|
1 | 确定性 | 给定一个集合,任给一个元素,该元素或者属于或者不属于该集合,二者必居其一。 |
2 | 互异性 | 一个集合中,任何两个元素都认为是不相同的,即每个元素只能出现一次。有时需要对同一元素出现多次的情形进行刻画,可以使用多重集。 |
3 | 无序性 | 一个集合中,每个元素的地位都是相同的,元素之间是无序的。集合上可以定义序关系,定义了序关系后,元素之间就可以按照序关系排序。但就集合本身的特性而言,元素之间没有必然的序。 |
1.2 C++中的集合
序号 | 特点 |
---|---|
1 | 不具备无序性,有序性(默认:升序) |
2 | set特点值唯一。 |
3 | s[0]等不能随机访问 |
4 | 四个迭代器(可变&const) |
2. 常见操作
(1)集合按照顺序插入,且唯一;
s.insert(-1); //(1)集合按照顺序插入,且唯一;
(2)count()返回1:存在;0:不存在。
cout << s.count(3) << endl; //(2)count()返回1:存在;0:不存在。
(3.1)find()如果没有找到,指针移动到末尾;找到的话,可以用指针解引用,
(3.2)禁止修改
cout << (it3 == s.end()) << endl; //(3.1)find()如果没有找到,指针移动到末尾;找到的话,可以用指针解引用,
auto it4 = s.find(4);
cout << *it4 << endl;
// *it4 = 100; // (3.2)禁止修改
(4)如果集合为类对象, set存放的对象必须可以比较大小,所以类必须重载<,==
//(class)
bool operator<(const Simple&)const{
return true;
}
//(main)
set<Simple> ss;// (4)如果集合为类对象, set存放的对象必须可以比较大小,所以类必须重载<,==
- 完整案例
#include <set>
#include <vector>
#include <iostream>
using namespace std;
void Print(const set<int>& s){
for(auto n:s){
cout << n << " ";
}
cout << endl;
}
class Simple{
public:
/*
bool operator==(const Simple&)const{
return true;
}*/
bool operator<(const Simple&)const{
return true;
}
};
int main(){
set<Simple> ss;// (4)如果集合为类对象, set存放的对象必须可以比较大小,所以类必须重载<,==
ss.insert(Simple());
set<int> s = {10,2,2,2,2,13,4,15,6};
// 数据有序
// 数据唯一
Print(s);
s.insert(-1);
Print(s);
s.insert(-1);
s.insert(-1); //(1)按照顺序插入,且唯一;
Print(s);
// s[0]; 不能随机访问
// 四个迭代器
// set<int>::reverse_iterator it = s.rbegin();
auto it = s.rbegin();
while(it != s.rend()){
cout << *it++ << ",";
}
cout << endl;
cout << s.size() << endl;
s.erase(-1);
Print(s);
cout << s.count(3) << endl; //(2)count()返回1:存在;0:不存在。
cout << s.count(4) << endl;
auto it3 = s.find(3);
cout << (it3 == s.end()) << endl; //(3.1)find()如果没有找到,指针移动到末尾;找到的话,可以用指针解引用,
auto it4 = s.find(4);
cout << *it4 << endl;
// *it4 = 100; // (3.2)禁止修改,
}
3. 面试常考
作用:去重+ 排序
3.1(287vector插入到set)寻找重复数
寻找重复数
关键: 用count进行计数,将vector插入到set里面
- 完整代码
int findDuplicate(vector<int>& nums) {
set<int> s;
for(auto n:nums){
if(s.count(n)==0){ //用count进行计数,将vector插入到set里面
s.insert(n);
}else{
return n;
}
}
return -1;
}
3.2(414 vector转化为set)寻找第三大数
寻找第三大数
(1)向量转化为集合的方法
set<int> s(nums.begin(),nums.end()); //(1)向量转化为集合的方法
(2)返回值
auto it = s.end();
if(s.size() < 3){
// return *(--it); //(2)返回最大数
return *prev(it,1);
}else{
return *prev(it,3); //(3)尾指针向前挪动三个,第三大数
}
- 完整代码
int thirdMax(vector<int>& nums) {
set<int> s(nums.begin(),nums.end()); //(1)向量转化为集合的方法
auto it = s.end();
if(s.size() < 3){
// return *(--it); //(2)返回最大数
return *prev(it,1);
}else{
return *prev(it,3); //(3)尾指针向前挪动三个,第三大数
}
}
4. 仿函数+模板+set
4.1. 仿函数作为set类的比较操作模板
(1)在模板参数中添加仿函数类可以改变set升降序,实质:重载()运算符
//class
class Greator{ //(1) 在模板参数中添加仿函数类可以改变set升降序;
public:
bool operator()(int a,int b){
return a > b;
}
};
//main()
set<int,Greator> s; // 在模板参数中添加仿函数类可以改变set升降序
(2)可以多定义几个仿函数类,用来调用主类的成员函数;
//class
class AgeComp{ //(2)可以多定义几个仿函数类,用来调用主类的成员函数;
public:
bool operator()(const Student& a,const Student& b){
return a.GetAge() > b.GetAge();
}
};
class ScoreComp{ //(2)可以多定义几个仿函数类,用来调用主类的成员函数;
public:
bool operator()(const Student& a,const Student& b){
return a.GetScores() < b.GetScores();
}
};
//main()
set<Student,AgeComp> stu = {
Student("张三",21,89),
Student("李四",19,78),
Student("王五",20,69)
};
Print(stu);
set<Student,ScoreComp> stu2(stu.begin(),stu.end()); //(2)仿函数的调用:实质:重载()运算符;
- 完整案例
#include <set>
#include <vector>
#include <iostream>
using namespace std;
//(1) 在模板参数中添加仿函数类可以改变set升降序;
//(2)可以多定义几个仿函数类,用来调用主类的成员函数;
template<typename T,typename S>
void Print(const set<T,S>& s){
for(auto n:s){
cout << n << " ";
}
cout << endl;
}
class Greator{ //(1) 在模板参数中添加仿函数类可以改变set升降序;
public:
bool operator()(int a,int b){
return a > b;
}
};
class Student{
string name;
int age;
float scores;
public:
Student(const string& name,int age,float scores):name(name),age(age),scores(scores){}
int GetAge()const{return age;}
float GetScores()const{return scores;}
friend ostream& operator<<(ostream& os,const Student& s){
return os << s.name << ',' << s.age << ',' << s.scores;
}
};
class AgeComp{ //(2)可以多定义几个仿函数类,用来调用主类的成员函数;
public:
bool operator()(const Student& a,const Student& b){
return a.GetAge() > b.GetAge();
}
};
class ScoreComp{ //(2)可以多定义几个仿函数类,用来调用主类的成员函数;
public:
bool operator()(const Student& a,const Student& b){
return a.GetScores() < b.GetScores();
}
};
int main(){
set<int,Greator> s; // 在模板参数中添加仿函数类可以改变set升降序
s = {10,2,2,2,2,13,4,15,6};
// 数据有序
// 数据唯一
Print(s);
set<Student,AgeComp> stu = {
Student("张三",21,89),
Student("李四",19,78),
Student("王五",20,69)
};
Print(stu);
set<Student,ScoreComp> stu2(stu.begin(),stu.end()); //(2)仿函数的调用:实质:重载()运算符;
Print(stu2);
}
4.2 函数指针的应用
(1)常用函数指针,实现数据的批量处理;声明的写法;
typedef void (*func_t)(int& n); //a.常用函数指针,实现数据的批量处理;
void Travel(vector<int>& vec, func_t func){ //声明的写法;
for(auto& n:vec){
func(n);
}
}
(2)调用的写法,多个函数并用;
Travel(vec,Double); //b。调用的写法,多个函数并用;
- 完整案例
#include <iostream>
#include <vector>
using namespace std;
void Print(int& n){
cout << n << "\t";
}
void Double(int& n){
n*=n;
}
typedef void (*func_t)(int& n); //a.常用函数指针,实现数据的批量处理;
void Travel(vector<int>& vec, func_t func){ //声明的写法;
for(auto& n:vec){
func(n);
}
}
int main()
{
vector<int> vec = {1,2,3,4};
Travel(vec,Print);
cout << endl;
Travel(vec,Double); //b。调用的写法,多个函数并用;
Travel(vec,Print);
return 0;
}
4.3 函数指针+模板函数的应用
(1)常用函数指针,实现数据的批量处理;声明的写法;
引入了函数模板;
typedef void (*func_t)(int& n); //a.常用函数指针,实现数据的批量处理;
template<typename T, typename F>
void Travel(T vec, F func){ //声明的写法;
for(auto n:vec){ //引用通常为const类型,因为n进行修改,所以采用n
func(n);
}
}
- 完整案例
#include <iostream>
#include <vector>
#include <list>
#include <set>
using namespace std;
template<typename T>
void Print(T& n){
cout << n << "\t";
}
void Double(int& n){
n*=n;
}
typedef void (*func_t)(int& n); //a.常用函数指针,实现数据的批量处理;
template<typename T, typename F>
void Travel(T vec, F func){ //声明的写法;
for(auto n:vec){ //引用通常为const类型,因为n进行修改,所以采用n
func(n);
}
}
int main()
{
vector<int> vec = {1,2,3,4};
Travel(vec,Print<int>);
cout << endl;
Travel(vec,Double); //b。调用的写法,多个函数并用;
Travel(vec,Print<int>);
list<float> l = {1.1,2.2,3.3};
Travel(l,Print<float>);
cout << endl;
//Travel(vec,Double); //b。调用的写法,多个函数并用;
// Travel(vec,Print<float>);
set<int> s = {1,2};
Travel(s,Print<int>);
cout << endl;
Travel(s,Double); //b。调用的写法,多个函数并用;
Travel(s,Print<int>);
return 0;
}
4.4 函数指针+模板函数+仿函数的应用
打印容器对象的5种方式:
CDisplay<int> display;
//display.operator()(n);
display(n); //写法1
CDisplay<int>()(n); //写法2
Travesal(s,display);// 写法3:(1)函数对象,仿函数
Travesal(s,CDisplay<int>()); //写法4:(2) 匿名对象,仿函数
Travesal(s,[](int n){cout << n << ',';}); //写法5/lamada的写法,表示仿函数,注意引入的n为局部参数;
- 完整案例
#include <iostream>
#include <vector>
#include <list>
#include <set>
using namespace std;
template<typename T>
void Display(T& n){
cout << n << " ";
}
void Double(int& n){
n*=n;
}
// C语言:面向过程编程
// 函数指针:函数式编程
// C++语言:面向对象编程
// 模板:泛型编程
template<typename T>
class CDisplay{
public:
void operator()(T& n)const{
cout << n << " ";
}
};
// typedef void (*func_t)(int& n);
template<typename T,typename F>
void Travesal(T& vec,F func){
for(auto n:vec){
//(*func)(n);
func(n);
}
}
template<typename T>
class Test{
T display;
};
int main(){
vector<int> vec = {1,2,3,4,5,6};
Travesal(vec,Display<int>);
cout << endl;
//
// Travesal(vec,Double);
Travesal(vec,Display<int>);
cout << endl;
vector<float> vec2 = {1.1,2.2,3.3,4.4,5.5,6.6};
Travesal(vec2,Display<float>);
cout << endl;
list<int> li = {1,2,3,4,5,6};
Travesal(li,Display<int>);
cout << endl;
set<int> s = {1,2,3,4,5,6};
Travesal(s,Display<int>);
cout << endl;
int n = 100;
Display(n);
CDisplay<int> display;
//display.operator()(n);
display(n); //写法1
CDisplay<int>()(n); //写法2
Travesal(s,display);// 写法3:(1)函数对象,仿函数
Travesal(s,CDisplay<int>()); //写法4:(2) 匿名对象,仿函数
Travesal(s,[](int n){cout << n << ',';}); //写法5/lamada的写法,表示仿函数,注意引入的n为局部参数;
}
5. 标准
set 特点值唯一。
5.1 初始化
- 默认构造(可带参数)
- 复制构造
- 范围赋值构造
5.2 基本操作
- 迭代器
迭代器 | 作用 |
---|---|
c.begin() | 头迭代器 |
c.end() | 尾迭代器 |
c.rbegin() | 反向头迭代器 |
c.rend() | 反向尾迭代器 |
与vector相似。
- 数据量操作
函数 | 作用 |
---|---|
c.size() | 大小 |
c.max_size() | 最大大小 |
c.empty() | 判空 |
c.clear() | 清空 |
5.3 添加数据
- insert插入数据
#include <iostream>
#include <set>
#include <algorithm>
using namespace std;
void Display(int val){
cout << val << endl;
}
int main(){
set<int> m;
for(int i=0;i<10;i++){
m.insert(i);
}
for_each(m.begin(),m.end(),Display);
}
通过返回值 pair<iterator,bool> 判断插入数据是否成功。
- insert指定位置插入数据
#include <iostream>
#include <set>
#include <algorithm>
using namespace std;
void Display(int val){
cout << val << endl;
}
int main(){
set<int> m;
for(int i=0;i<10;i++){
m.insert(i);
}
m.insert(m.begin(),100);
for_each(m.begin(),m.end(),Display);
}
插入数据总是认为成功,如果已经存在,则返回已经存在的位置迭代器,否则,添加新的值,且返回位置迭代器。
- insert插入范围数据
5.4 遍历
- 迭代器 for 循环
for(set::iterator it = m.begin();it != m.end();it++){
cout << *it << endl;
}
* `for_each()`循环\[推荐\]
定义函数指针
inline void Display(int val){
cout << val << endl;
}
执行for_each
for_each(m.begin(),m.end(),Display);
- C++11 auto 迭代器写法
for(auto it = m.begin();it != m.end();it++){
cout << *it << endl;
}
- C++11 for-loop-scope迭代器写法[推荐]
for(auto p : m){
cout << p << endl;
}
- C++11 for_each()与lamdba表达式
for_each(m.begin(),m.end(),[](int p){
cout << p << endl;
});
5.5 查找
- count()判断值是否存在
if(m.count(val) == 1){
...
}
- find()判断值是否存在以及位置
set<int>::iterator it = m.find(val);
if(m.end() != it){
...
}
5.6 区域查找
成员变量 | 作用 |
---|---|
m.lower_bound(val) | val下边界 |
m.upper_bound(val) | val上边界 |
m.equal_range(val) | val上下边界 |
5.7 删除
- 关键字删除
m.erase(val);
- 迭代器删除
m.erase(m.begin());
- 区域删除
m.erase(it_a,it_b);
5.8 排序
默认按照 val 升序排列。自定义排序时,可以在实例化加上 val 的 comp 仿函数或者重载<运算符。
set<value类型,comp> m;