C++基础11:STL容器set

1. 简介

1.1 数学中的集合

No.特性说明
1确定性给定一个集合,任给一个元素,该元素或者属于或者不属于该集合,二者必居其一。
2互异性一个集合中,任何两个元素都认为是不相同的,即每个元素只能出现一次。有时需要对同一元素出现多次的情形进行刻画,可以使用多重集。
3无序性一个集合中,每个元素的地位都是相同的,元素之间是无序的。集合上可以定义序关系,定义了序关系后,元素之间就可以按照序关系排序。但就集合本身的特性而言,元素之间没有必然的序。

1.2 C++中的集合

序号特点
1不具备无序性,有序性(默认:升序)
2set特点值唯一。
3s[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 初始化

  1. 默认构造(可带参数)
  2. 复制构造
  3. 范围赋值构造

5.2 基本操作

  • 迭代器
迭代器作用
c.begin()头迭代器
c.end()尾迭代器
c.rbegin()反向头迭代器
c.rend()反向尾迭代器

与vector相似。

  • 数据量操作
函数作用
c.size()大小
c.max_size()最大大小
c.empty()判空
c.clear()清空

5.3 添加数据

  1. 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> 判断插入数据是否成功。

  1. 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);
    }

插入数据总是认为成功,如果已经存在,则返回已经存在的位置迭代器,否则,添加新的值,且返回位置迭代器。

  1. 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 查找

  1. count()判断值是否存在
if(m.count(val) == 1){
     ...
}
  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 删除

  1. 关键字删除
 m.erase(val);
  1. 迭代器删除
m.erase(m.begin());
  1. 区域删除
m.erase(it_a,it_b);

5.8 排序

默认按照 val 升序排列。自定义排序时,可以在实例化加上 val 的 comp 仿函数或者重载<运算符。

set<value类型,comp> m;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值