c++--------《set 和 map》

打印 上一主题 下一主题

主题 991|帖子 991|积分 2973

1 set系列的使⽤

1.1 set类的先容

  1. • set的声明如下,T就是set底层关键字的类型
  2. • set默认要求T⽀持⼩于⽐较,如果不⽀持或者想按⾃⼰的需求⾛可以⾃⾏实现仿函数传给第⼆个模版参数
  3. • set底层存储数据的内存是从空间配置器申请的,如果需要可以⾃⼰实现内存池,传给第三个参数。
  4. • ⼀般情况下,我们都不需要传后两个模版参数。
  5. • set底层是⽤红⿊树实现,增删查效率是 ,迭代器遍历是⾛的搜索树的中序,所以是有序的。O(logN)
  6. • 前⾯部分我们已经学习了vector/list等容器的使⽤,STL容器接⼝设计,⾼度相似,所以这⾥我  们就不再⼀个接⼝⼀个接⼝的介绍,⽽是直接带着⼤家看⽂档,挑⽐较重要的接⼝进⾏介绍。
复制代码
  1. template < class T, // set::key_type/value_type类型
  2. class Compare = less<T>, // set::key_compare/value_compare比较函数
  3. class Alloc = allocator<T> // set::allocator_type
  4. > class set;
复制代码
1.2 set的构造和迭代器


  1. ~empty(1) 无参构造   set()
  2. ~range(2) 迭代器构造 set(InputIterator first,InputIterator second)
  3. ~copy(3)  拷贝构造   set(const set& x)
  4. ~move(4) 移动构造    set(set&& x)
  5. ~initializer list(5) set({1,2,3,4,5})  
复制代码
  1. // empty (1) ⽆参默认构造
  2. explicit set (const key_compare& comp = key_compare(),
  3. const allocator_type& alloc = allocator_type());
  4. // range (2) 迭代器区间构造
  5. template <class InputIterator>
  6. set (InputIterator first, InputIterator last,
  7. const key_compare& comp = key_compare(),
  8. const allocator_type& = allocator_type());
  9. // copy (3) 拷⻉构造
  10. set (const set& x);
  11. // initializer list (5) initializer 列表构造
  12. set (initializer_list<value_type> il,
  13. const key_compare& comp = key_compare(),
  14. const allocator_type& alloc = allocator_type());
  15. // 迭代器是⼀个双向迭代器
  16. iterator -> a bidirectional iterator to const value_type
  17. // 正向迭代器
  18. iterator begin();
  19. iterator end();
  20. // 反向迭代器
  21. reverse_iterator rbegin();
  22. reverse_iterator rend();
复制代码
1.3 set重要接口

1: insert (插入元素)
  1. // 单个数据插⼊,如果已经存在则插⼊失败,bool返回false
  2. pair<iterator,bool> insert (const value_type& val);
  3. //初始化列表插入,已经在容器中存在的值不会插入
  4. void insert(initializer_list<value_type> x)
  5. //迭代器区间插入,如果已经存在的值就不会插入
  6. template<class InputIterator>
  7. void insert(InputIterator first,InputIterator last);
复制代码
2:find(查找元素)
  1. //查找val,返回val所在的迭代器,没有找到返回end()
  2. iterator find(const value_type& val);
复制代码
3:count (查找元素个数)
  1. //查找val,返回val的个数
  2. size_type count(const value_type& val) const;
复制代码
4:erase(删除元素)
  1. // 删除⼀个迭代器位置的值
  2. iterator erase (const_iterator position);
  3. // 删除val,val不存在返回0,存在返回1
  4. size_type erase (const value_type& val);
  5. // 删除⼀段迭代器区间的值
  6. iterator erase (const_iterator first, const_iterator last);
复制代码
5:lower_bound 和 upper_bound
返回(upper_bound)的大于val的迭代器
返回(lower_bound)的大于等于val的迭代器
  1. // 返回⼤于等val位置的迭代器
  2. iterator lower_bound (const value_type& val) const;
  3. // 返回⼤于val位置的迭代器
  4. iterator upper_bound (const value_type& val) const;
复制代码
2 实现样例

2.1: insert和迭代器遍历使⽤样例:

  1. #include<iostream>
  2. #include<set>
  3. using namespace std;
  4. int main()
  5. {
  6. // 去重+升序排序
  7. set<int> s;
  8. // 去重+降序排序(给⼀个⼤于的仿函数)
  9. //set<int, greater<int>> s;
  10. s.insert(4);
  11. s.insert(3);
  12. s.insert(65);
  13. s.insert(8);
  14. //set<int>::iterator it = s.begin();
  15. auto it = s.begin();
  16. while (it != s.end())
  17. {
  18. // error C3892: “it”: 不能给常量赋值
  19. // *it = 1;//数据不能被修改
  20. cout << *it << " ";
  21. ++it;
  22. }
  23. cout << endl;
  24. // 插⼊⼀段initializer_list列表值,已经存在的值插⼊失败
  25. s.insert({ 2,8,3,9 });
  26. for (auto e : s)
  27. {
  28. cout << e << " ";
  29. }
  30. cout << endl;
  31. set<string> strset = { "sort", "insert", "add" };
  32. // 遍历string⽐较ascll码⼤⼩顺序遍历的
  33. for (auto& e : strset)
  34. {
  35. cout << e << " ";
  36. }
  37. cout << endl;
  38. }
复制代码
【注意】
如果可以改变比力大小次序 greater降序 ,默认是升序
  1. set<string,greater<string>> strset = { "sort", "insert", "add" };
  2. // 遍历string⽐较ascll码⼤⼩顺序遍历的
  3. for (auto& e : strset)
  4. {
  5. cout << e << " ";
  6. }
  7. cout << endl;
  8. }
复制代码

2.2: find和erase使⽤样例:

  1. #include<iostream>
  2. #include<set>
  3. using namespace std;
  4. int main()
  5. {
  6. set<int> s = { 4,2,7,2,8,5,9 };
  7. for (auto e : s)
  8. {
  9. cout << e << " ";
  10. }
  11. cout << endl;
  12. // 删除最⼩值
  13. s.erase(s.begin());
  14. for (auto e : s)
  15. {
  16. cout << e << " ";
  17. }
  18. cout << endl;
  19. // 直接删除x
  20. int x;
  21. cin >> x;
  22. int num = s.erase(x);
  23. if (num == 0)
  24. {
  25. cout << x << "不存在!" << endl;
  26. }
  27. for (auto e : s)
  28. {
  29. cout << e << " ";
  30. }
  31. cout << endl;
  32. // 直接查找在利⽤迭代器删除x
  33. cin >> x;
  34. auto pos = s.find(x);
  35. if (pos != s.end())
  36. {
  37. s.erase(pos);
  38. }
  39. else
  40. {
  41. cout << x << "不存在!" << endl;
  42. }
  43. for (auto e : s)
  44. {
  45. cout << e << " ";
  46. }
  47. cout << endl;
  48. // 算法库的查找 O(N)
  49. auto pos1 = find(s.begin(), s.end(), x);
  50. // set⾃⾝实现的查找 O(logN)
  51. auto pos2 = s.find(x);
  52. // 利⽤count间接实现快速查找
  53. cin >> x;
  54. if (s.count(x))
  55. {
  56. cout << x << "在!" << endl;
  57. }
  58. else
  59. {
  60. cout << x << "不存在!" << endl;
  61. }
  62. return 0;
  63. }
  64. #include<iostream>
  65. #include<set>
  66. using namespace std;
  67. int main()
  68. {
  69. std::set<int> myset;
  70. for (int i = 1; i < 10; i++)
  71. myset.insert(i * 10); // 10 20 30 40 50 60 70 80 90
  72. for (auto e : myset)
  73. {
  74. cout << e << " ";
  75. }
  76. cout << endl;
  77. // 实现查找到的[itlow,itup)包含[30, 60]区间
  78. // 返回 >= 30
  79. auto itlow = myset.lower_bound(30);
  80. // 返回 > 60
  81. auto itup = myset.upper_bound(60);
  82. // 删除这段区间的值
  83. myset.erase(itlow, itup);
  84. for (auto e : myset)
  85. {
  86. cout << e << " ";
  87. }
  88. cout << endl;
  89. return 0;
  90. }
复制代码

   对于erase来说,删除后迭代器会失效
  练习

1: 俩个数组的交集
  1. 利用set的性质(避免多个数据重复)可以很好解决此问题
复制代码
  1. class Solution {
  2. public:
  3. vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
  4. set<int> s1(nums1.begin(), nums1.end());
  5. set<int> s2(nums2.begin(), nums2.end());
  6. // 因为set遍历是有序的,有序值,依次⽐较
  7. // ⼩的++,相等的就是交集
  8. vector<int> ret;
  9. auto it1 = s1.begin();
  10. auto it2 = s2.begin();
  11. while(it1 != s1.end() && it2 != s2.end())
  12. {
  13. if(*it1 < *it2)
  14. {
  15. it1++;
  16. }
  17. else if(*it1 > *it2)
  18. {
  19. it2++;
  20. }
  21. else
  22. {
  23. ret.push_back(*it1);
  24. it1++;
  25. it2++;
  26. }
  27. }
  28. return ret;
  29. }
  30. };
复制代码
2:环形链表
  1. 利用接口count可以解决环形链表问题
复制代码
  1. class Solution {
  2. public:
  3.     ListNode *detectCycle(ListNode *head)
  4.     {
  5.      set<ListNode*> s;
  6.      ListNode* cur=head;
  7.      while(cur)
  8.      {
  9.        if(s.count(cur))//s.count(cur)是判断有无cur的值
  10.        {
  11.          return cur;
  12.        }
  13.        else
  14.        s.insert(cur);
  15.        cur=cur->next;
  16.      }
  17.      return nullptr;
  18.     }
  19. };
复制代码
随机链表复制
  1. class Solution {
  2. public:
  3.     Node* copyRandomList(Node* head)
  4.     {
  5.         Node* copyhead=nullptr;
  6.         Node* copytail=nullptr;
  7.         Node* cur=head;
  8.         map<Node*,Node*> s;
  9.       while(cur)//结点映射拷贝
  10.       {
  11.         if(copytail==nullptr)
  12.         {
  13.         copyhead=copytail= new Node(cur->val);
  14.         }
  15.         else
  16.         {
  17.         copytail->next=new Node(cur->val);
  18.         copytail=copytail->next;
  19.         }
  20.         s[cur]=copytail;//结点映射 cur->random
  21.         cur=cur->next;
  22.       }
  23.        cur=head;
  24.        Node* copy=copyhead;
  25. while(cur)//random复制
  26.       {
  27.         if(cur->random==nullptr)
  28.         {
  29.             copy->random=nullptr;
  30.         }
  31.         else
  32.         {
  33.             copy->random=s[cur->random];
  34.         }
  35.         cur=cur->next;   
  36.         copy=copy->next;
  37.    
  38.       }
  39.       return copyhead;
  40.     }
  41. };
复制代码
3.map系列的使用

3.1 map类的先容

   map的声明如下,Key就是map底层关键字的范例,T是map底层value的范例,set默认要求Key⽀持⼩于⽐较,如果不⽀持或者需要的话可以⾃⾏实现仿函数传给第⼆个模版参数,map底层存储数据的内存是从空间设置器申请的。⼀般环境下,我们都不需要传后两个模版参数。map底层是⽤红⿊树实现,增删查改效率是 O(logN) ,迭代器遍历是⾛的中序,所以是按key有序次序遍历的。
  1. //Key就是map底层关键字的类型,T是map底层value的类型,
  2. template < class Key, // map::key_type
  3. class T, // map::mapped_type
  4. class Compare = less<Key>, // map::key_compare
  5. class Alloc = allocator<pair<const Key,T> > //
  6. map::allocator_type> class map;
复制代码
3.1.1 pair范例先容

  1. pair类型是用struct 封装的类,有俩个值key或value
复制代码
  1. typedef pair<const Key, T> value_type;
  2. template <class T1, class T2>
  3. struct pair
  4. {
  5. typedef T1 first_type;
  6. typedef T2 second_type;
  7. T1 first;
  8. T2 second;
  9. pair(): first(T1()), second(T2())
  10. {}
  11. pair(const T1& a, const T2& b): first(a), second(b)
  12. {}
  13. template<class U, class V>
  14. pair (const pair<U,V>& pr): first(pr.first), second(pr.second)
  15. {}
  16. };
复制代码
3.2 map的数据修改

map的构造与set的构造,和接口都差不多
增删查改
Insert

  1. 对于insert的返回值pair<iterator,bool>
  2. (1) single element  给值插入
  3. (2) with hint 指定位置插入
  4. (3) range 迭代器区间插入
  5. (4)initializer list 初始化插入
  6. 1:对于插入失败,则返回pair<iterator,false>,返回false
  7. 2:对于迭代器iterator,返回值iterator是返回插入时的迭代器
复制代码
  1. // 单个数据插⼊,如果已经key存在则插⼊失败,key存在相等value不相等也会插⼊失败
  2. pair<iterator,bool> insert (const value_type& val);
  3. // 列表插⼊,已经在容器中存在的值不会插⼊
  4. void insert (initializer_list<value_type> il);
  5. // 迭代器区间插⼊,已经在容器中存在的值不会插⼊
  6. template <class InputIterator>
  7. void insert (InputIterator first, InputIterator last);
复制代码
erase

  1. 对于erase返回值:
  2. 1:iterator 返回的是删除的迭代器
  3. 2:size_type 是返回删除的个数
  4. 对于参数
  5. 1:const iterator position 是 指定区间删除
  6. 2:const key_type& k 指定值删除
  7. 3: 迭代器区间删除
复制代码
  1. // 删除⼀个迭代器位置的值
  2. iterator erase (const_iterator position);
  3. // 删除k,k存在返回0,存在返回1
  4. size_type erase (const key_type& k);
  5. // 删除⼀段迭代器区间的值
  6. iterator erase (const_iterator first, const_iterator last);
复制代码
find、count


  1. iterator find (const key_type& k);
  2. // 查找k,返回k的个数
  3. size_type count (const key_type& k) const;
复制代码
operator[ ] ()
功能增 查 改

  1. map的operator【】,给key会返回value,如果key不存在的时候,
  2. 1:如果有就会修改second、2: key 不存在就插入
复制代码
1、如果k不在map中,insert会插⼊k和mapped_type默认值,同时[]返回结点中存储
mapped_type值的引⽤,那么我们可以通过引⽤修改返映射值。所以[]具备了插⼊+修改功能
2、如果k在map中,insert会插⼊失败,但是insert返回pair对象的first是指向key结点的迭代器,返回值同时[]返回结点中存储mapped_type值的引⽤,所以[]具备了查找+修改的功能.

数据修改
   前⾯我提到map⽀持修改mapped_type 数据,不⽀持修改key数据,修改关键字数据,破坏了底层搜刮树的布局。map第⼀个⽀持修改的⽅式时通过迭代器,迭代器遍历时或者find返回key所在的iterator修改,map还有⼀个⾮常重要的修改接⼝operator[],但是operator[]不但仅⽀持修改,还⽀持插⼊数据和查找数据,所以他是⼀个多功能复合接⼝需要注意从内部实现⻆度,map这⾥把我们传统说的value值,给的是T范例,typedef为mapped_type。⽽value_type是红⿊树结点中存储的pair键值对值。⽇常使⽤我们还是风俗将这⾥的T映射值叫做value。
  【注意】
1、如果key已经在map中,插⼊失败,则返回⼀个pair<iterator,bool>对象,返回pair对象first是key所在结点的迭代器,second是false
2、如果key不在在map中,插⼊成功,则返回⼀个pair<iterator,bool>对象,返回pair对象first是新插⼊key所在结点的迭代器,second是true
3、也就是说⽆论插⼊成功还是失败,返回pair<iterator,bool>对象的first都会指向key所在的迭代器
4、那么也就意味着insert插⼊失败时充当了查找的功能,正是因为这⼀点,insert可以⽤来实现operator[]

3.3multimap和map的差异

   multimap和map的使⽤基本完全类似,重要区别点在于multimap⽀持关键值key冗余,那么insert/find/count/erase都围绕着⽀持关键值key冗余有所差异,这⾥跟set和multiset完全⼀样,⽐如find时,有多个key,返回中序第⼀个。其次就是multimap不⽀持[],因为⽀持key冗余,[]就只能⽀持插⼊了,不能⽀持修改。
  竣事!!!

压力不是有人比你积极,而是比你牛叉几倍的人依然在积极。每个优秀的人,都有一段沉默的韶光继续就讲到这里谢谢各人的观看!!!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

农妇山泉一亩田

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表