• C++(四十九) — set、multiset 容器的基本操作


     1、set的基础知识

      set的特性是:所有元素都会根据元素的键值自动排序,set的元素不像map那样可以同时拥有实值(value)和键值(key),set元素的键值就是实值,实值就是键值。set不允许两个元素有相同的键值

      set的各成员函数列表如下:

    1. begin():返回指向第一个元素的迭代器;end():返回指向最后一个元素的迭代器;

    2.empty():如果集合为空,返回true;clear():清除所有元素;

    3. erase():删除集合中的元素

    • erase(iterator) :删除定位器iterator指向的值
    • erase(first,second):删除定位器first和second之间的值
    • erase(key_value):删除键值key_value的值

    4. count():返回某个值元素的个数,0或1;

    5. find():返回一个指向被查找到元素的迭代器,如果没找到则返回end()。

    6. insert():在集合中插入元素,插入函数的返回值不完全相同。

    • insert(key_value);将key_value插入到set中 ,返回值是pair<set<int>::iterator,bool>,bool标志着插入是否成功,而iterator代表插入的位置,若key_value已经在set中,则iterator表示的key_value在set中的位置。
    • inset(first,second);将定位器first到second之间的元素插入到set中,返回值是void.
    • set提供的插入函数:

      pair<iterator,bool> insert(const value_type& elem);
      iterator insert(iterator pos_hint, const value_type& elem);

    • multiset提供的插入函数:

        iterator insert(const value_type& elem);
           iterator insert(iterator pos_hint, const value_type& elem);

      返回值型别不同的原因是set不允许元素重复,而multiset允许。当插入的元素在set中已经包含有同样值的元素时,插入就会失败。所以set的返回值型别是由pair组织起来的两个值:第一个元素返回新元素的位置,或返回现存的同值元素的位置。第二个元素表示插入是否成功。

      set的第二个insert函数,如果插入失败,就只返回重复元素的位置!但是,所有拥有位置提示参数的插入函数的返回值型别是相同的。这样就确保了至少有了一个通用型的插入函数,在各种容器中有共通接口。

    7. lower_bound(key_value):返回第一个大于等于key_value的定位器;upper_bound(key_value),返回最后一个大于key_value的定位器

    8. size():集合中元素的数目;

    9. max_size():返回集合能容纳的元素的最大限值

    10. swap():交换两个集合变量,比如:c1.swap(c2);   swap(c1, c2);

    11. rbegin():返回指向集合中最后一个元素的反向迭代器.; rend():返回指向集合中第一个元素的反向迭代器

    12. equal_range()--返回集合中与给定值相等的上下限的两个迭代器

    13. get_allocator()--返回集合的分配器

    14. key_comp()--返回一个用于元素间值比较的函数

    15. upper_bound()--返回大于某个值元素的迭代器

    16. value_comp()--返回一个用于比较元素间的值的函数

      反向迭代器:set<int>::reverse_iterator 

      关于set有下面几个问题:

    (1)为何map和set的插入删除效率比用其他序列容器高?

      大部分人说,很简单,因为对于关联容器来说,不需要做内存拷贝和内存移动。说对了,确实如此。set容器内所有元素都是以节点的方式来存储,其节点结构和链表差不多,指向父节点和子节点。结构图可能如下:

      A
       /
      B C
     / /
      D E F G

    因此插入的时候只需要稍做变换,把节点的指针指向新的节点就可以了。删除的时候类似,稍做变换后把指向删除节点的指针指向其他节点也OK了。这里的一切操作就是指针换来换去,和内存移动没有关系。

    (2)为何每次insert之后,以前保存的iterator不会失效?

      iterator这里就相当于指向节点的指针,内存没有变,指向内存的指针怎么会失效呢(当然被删除的那个元素本身已经失效了)。相对于vector来说,每一次删除和插入,指针都有可能失效,调用push_back在尾部插入也是如此。因为为了保证内部数据的连续存放,iterator指向的那块内存在删除和插入过程中可能已经被其他内存覆盖或者内存已经被释放了。即使时push_back的时候,容器内部空间可能不够,需要一块新的更大的内存,只有把以前的内存释放,申请新的更大的内存,复制已有的数据元素到新的内存,最后把需要插入的元素放到最后,那么以前的内存指针自然就不可用了。特别时在和find等算法在一起使用的时候,牢记这个原则:不要使用过期的iterator。

    (3)当数据元素增多时,set的插入和搜索速度变化如何?

      如果你知道log2的关系你应该就彻底了解这个答案。在set中查找是使用二分查找,也就是说,如果有16个元素,最多需要比较4次就能找到结果,有32个元素,最多比较5次。那么有10000个呢?最多比较的次数为log10000,最多为14次,如果是20000个元素呢?最多不过15次。看见了吧,当数据量增大一倍的时候,搜索次数只不过多了1次,多了1/14的搜索时间而已。你明白这个道理后,就可以安心往里面放入元素了。

      还要注意begin() 和 end()函数是不检查set是否为空的,使用前最好使用empty()检验一下set是否为空.

     

     (4)set和multiset通常以红黑树完成,存在的优缺点?

      和所有关联式容器类似,通常使用平衡二叉树完成。事实上,set和multiset通常以红黑树实作而成。自动排序的优点是使得搜寻元素时具有良好的性能,具有对数时间复杂度。但是造成的一个缺点就是:

    • 不能直接改变元素值。因为这样会打乱原有的顺序。
    • 改变元素值的方法是:先删除旧元素,再插入新元素。
    • 存取元素只能通过迭代器,从迭代器的角度看,元素值是常数。

     2、set 比较函数、自定义比较函数

      比较函数:

       set<int> seta;               //默认是按升序方式排列元素
        set<int, less<int>> setA;    //按升序方式排列元素
    
        //C++ “greater”: 未声明的标识符错误
        //解决方案:在头文件中加入#include<functional>即可解决
        set<int, greater<int>> setB; //按降序方式排列元素

      自定义比较函数
        (1)元素不是结构体:
            例:
            //自定义比较函数myComp,重载“()”操作符

    1. struct myComp  
    2. {  
    3.     bool operator()(const your_type &a,const your_type &b)  
    4.     [  
    5.         return a.data-b.data>0;  
    6.     }  
    7. }  
    8. set<int,myComp>s;  
    9. ......  
    10. set<int,myComp>::iterator it;  
    #define  _CRT_SECURE_NO_WARNINGS
    #include <iostream>
    #include <algorithm>
    #include <set>
    using namespace std;
    
    
    class Student
    {
    public:
        Student(char *name, int age)
        {
            strcpy(this->name, name);
            this->age = age;
        }
    public:
        char name[64];
        int age;
    };
    // 仿函数
    struct FuncStu
    {
        bool operator()(const Student &left, const Student &right)
        {
            return (left.age < right.age);
        }
    };
    
    int main()
    {
        Student s1("s1", 1);
        Student s2("s1", 9);
        Student s3("s1", 7);
    
        set<Student, FuncStu> set1;
        set1.insert(s1);
        set1.insert(s2);
        set1.insert(s3);
        
        set<Student, FuncStu>::iterator it;
        for (it = set1.begin(); it != set1.end(); ++it)
        {
            cout << it->age << "  " << it->name << endl;
        }
    
        system("pause");
        return 0;
    }

        (2)如果元素是结构体,可以直接将比较函数写在结构体内。
            例:

     
      1. struct Info  
      2. {  
      3.     string name;  
      4.     float score;  
      5.     //重载“<”操作符,自定义排序规则  
      6.     bool operator < (const Info &a) const  
      7.     {  
      8.         //按score从大到小排列  
      9.         return a.score<score;  
      10.     }  
      11. }  
      12. set<Info> s;  
      13. ......  
      14. set<Info>::iterator it;  

     参考博客:https://blog.csdn.net/xiajun07061225/article/details/7459206

         https://www.cnblogs.com/ChinaHook/p/6985444.html#top

     

  • 相关阅读:
    再学 GDI+[91]: TGPImage(11) 转灰度图像
    再学 GDI+[90]: TGPImage(10) 获取图像的调色板信息
    给 Memo 排序的函数
    再学 GDI+[97]: TGPImage(17) 获取 GDI+ 所支持的可编码、可解码的图像格式
    再学 GDI+[94]: TGPImage(14) 增减图像的红、绿、蓝三色的成分
    上周热点回顾(10.2611.1)
    博客园上海俱乐部Windows 7社区发布活动的奖品
    顶吧!顶出今日头条
    博客园电子期刊2009年10月刊发布啦
    对于近期社区问题的一点想法
  • 原文地址:https://www.cnblogs.com/eilearn/p/10992783.html
Copyright © 2020-2023  润新知