【C++】-- STL之map和set详解(三)

简介: 【C++】-- STL之map和set详解

第三种方式:借助计数排序思想,使用operator[ ]运算符重载获取value

mapped_type& operator[] (const key_type& k);//返回k对应的value的引用

返回:

(*((this->insert(make_pair(k,mapped_type()))).first)).second

即:

1. mapped_type& operator[](const key_type& k)
2. {
3.  pair<iteerator, bool> ret = insert(make_pair(k, mapped_type()));
4.  return ret.first->second;
5. }

operator[ ]的本质就是插入:

①如果k不在map中,先插入<k,V( )>,返回节点中V对象的引用

②如果k已经在map中,返回k所在节点中对应V对象的引用

1.  //第三种统计方法-计数排序
2.  string arr[] = { "篮球","足球","排球","羽毛球","足球","乒乓球","足球","排球","羽毛球","篮球","足球" };
3.  map<string, int> countMap;
4. 
5.  for (const auto& str : arr)
6.  {
7.    countMap[str]++;//等价于countMap.operator[](str),会调用mapped type()的默认构造函数构造一个匿名对象。其中str是key,++的是key对应的value,即返回值
8.  }
9. 
10.   for (const auto& e : countMap)
11.   {
12.     cout << e.first << ":" << e.second << endl;
13.   }

这三种方式中,operator[ ]最简洁,更好使用。

计数结束后,排序有2种方式:

第一种排序:用sort排序

可以使用sort进行排序,先包含sort的头文件:

#include<algorithm>

vector里面如果要存pair,得把每个pair拿出来拷贝,再插入到vector中,而且string是深拷贝,代价大。

sort要排序,必须支持整数比较大小,传的是迭代器,迭代器指向的数据必须要能比较大小,v里面存的是pair,需要重载pair的比较大小。  sort的第3个参数是compare,要传对象,用实际对象推compare的类型,可以用仿函数比较,给sort的第3个参数传个匿名对象

sort的第3个参数是Compare比较方式,默认是按小于比较的,要找排名前面的球类,必须要传入大于的比较方式,就需要重新写仿函数:

1. template <class RandomAccessIterator, class Compare>
2.  void sort(RandomAccessIterator first, RandomAccessIterator last, Compare comp);

大于的比较方式的仿函数:

1. struct MapItCompare
2. {
3.  bool operator()(map<string, int>::iterator x, map<string, int>::iterator y)
4.  {
5.    return x->second > y->second;
6.  }
7. };
1. void Map_test2()
2. {
3.  string arr[] = { "篮球", "足球", "排球", "羽毛球", "足球", "乒乓球", "足球", "排球", "羽毛球", "篮球", "足球" };
4.  1.统计计数
5.  map<string, int> countMap;
6.  for (const auto& str : arr)
7.  {
8.    countMap[str]++;
9.  }
10. 
11. 2.排序(第一种用sort排序)
12.   vector<map<string, int>::iterator> v;
13.   map<string, int>::iterator countMapIt = countMap.begin();
14. 
15.   while (countMapIt != countMap.end())
16.   {
17.     v.push_back(countMapIt);//把迭代器放进vector中,不创建节点,不拷贝新数组
18.     countMapIt++;
19.   }
20.   sort(v.begin(), v.end(), MapItCompare());
21. }

统计计数完毕后:

排序完毕后:

这就找出了排名前3的球类。

第二种排序:用map排序:map这个结构是天生的可以用来排序的结构,不过map的比较方式默认是less,也就是按照升序排的:

1. template < class Key,                                     // map::key_type
2. class T,                                       // map::mapped_type
3. class Compare = less<Key>,                     // map::key_compare
4. class Alloc = allocator<pair<const Key,T> >    // map::allocator_type
5.            > class map;

但是现在需要按照降序排,就要传比较方式:

1.  //第二种排序,借助map排序
2.     map<int, string,greater<int>> sortMap;//将次数作为first,将字符串作为second,比较方式为大于greater
3.  for (auto e : countMap)
4.  {
5.    sortMap.insert(make_pair(e.second, e.first));
6.  }

由于int作为key,所以有相同key的球类只存第一种,篮球、排球和羽毛球的个数都是2(举的例子有点不恰当,应该往所有球类的个数都不相同),只会出现篮球,不会出现排球和羽毛球:

这种方式用了拷贝,如果不想拷贝的话,可以存到set中。

第三种排序方式:

给set的迭代器传个仿函数:

1. // 利用set排序  --不拷贝pair数据
2.  set<map<string, int>::iterator, MapItCompare> sortSet;//set传模板参数,传MapItCompare类型
3.  countMapIt = countMap.begin();
4.  while (countMapIt != countMap.end())
5.  {
6.    sortSet.insert(countMapIt);
7.    ++countMapIt;
8.  }

第四种比较方式: 用优先级队列,向优先级队列中存放迭代器,减少拷贝,要找个数最大的,要建小堆

1. struct MapItCompare
2. {
3.  bool operator()(map<string, int>::iterator x, map<string, int>::iterator y)
4.  {
5.    return x->second < y->second;
6.  }
7. };
1.  typedef map<string, int>::iterator MapIt;
2.  priority_queue<MapIt, vector<MapIt>, MapItCompare> pq;
3. 
4.  map<string, int>::iterator countMapIt = countMap.begin();
5.  while (countMapIt != countMap.end())
6.  {
7.    pq.push(countMapIt);
8.    countMapIt++;
9.  }

堆顶元素就是找到的个数最多的球类,足球:

(4)删除erase( )

size_type erase (const key_type& k);//通过k删除
1.  map<string, string> dict;
2.  dict.insert(make_pair("spring", "春天"));
3.  dict["spring"] += "、温泉";
4.  dict["summer"] = "夏天";
5.  dict["autumn"] = "秋天";
6.  dict["winter"] = "冬天";
7. 
8.  auto mit = dict.begin();//同map<string, int>::iterator mit = m.begin();
9.  while (mit != dict.end())
10.   {
11.     cout << mit->first << ":" << mit->second << endl;
12.     mit++;
13.   }
14.   cout << endl;

dict.erase("spring");

再遍历打印:

四、multiset 和 multimap

set中和map中存储的元素不可以重复,但是multiset和multimap中的元素是可以重复的,它们中的元素不可以修改,但是可以插入和删除:

multiset:头文件为#include<set>

1. template < class T,                        // multiset::key_type/value_type
2. class Compare = less<T>,        // multiset::key_compare/value_compare
3. class Alloc = allocator<T> >    // multiset::allocator_type
4.            > class multiset;
1.     multiset<int> mls1;
2.  int arr[] = { 2,3,2,5,3,3};
3. 
4.  mls1.insert(arr, arr + 6);
5.  for (auto e : mls1)
6.  {
7.    cout << e << endl;
8.  }

允许存在key相同的元素:

 

multimap:头文件为#include<map>

1. template < class Key,                                     // multimap::key_type
2. class T,                                       // multimap::mapped_type
3. class Compare = less<Key>,                     // multimap::key_compare
4. class Alloc = allocator<pair<const Key,T> >    // multimap::allocator_type
5.            > class multimap;
1.  multimap<string,string> mlp1;
2.  mlp1.insert(make_pair("spring", "春天"));
3.  mlp1.insert(make_pair("summer", "夏天"));
4.  mlp1.insert(make_pair("autumn", "秋天"));
5.  mlp1.insert(make_pair("winter", "冬天"));
6. 
7.  mlp1.insert(make_pair("spring", "温泉"));
8. 
9.  for (auto e : mlp1)
10.   {
11.     cout << e.first << ":" << e.second << endl;
12.   }

允许存在key相同的pair:

相关文章
|
1月前
|
算法
你对Collection中Set、List、Map理解?
你对Collection中Set、List、Map理解?
64 18
你对Collection中Set、List、Map理解?
|
9天前
|
编译器 C语言 C++
【c++丨STL】list模拟实现(附源码)
本文介绍了如何模拟实现C++中的`list`容器。`list`底层采用双向带头循环链表结构,相较于`vector`和`string`更为复杂。文章首先回顾了`list`的基本结构和常用接口,然后详细讲解了节点、迭代器及容器的实现过程。 最终,通过这些步骤,我们成功模拟实现了`list`容器的功能。文章最后提供了完整的代码实现,并简要总结了实现过程中的关键点。 如果你对双向链表或`list`的底层实现感兴趣,建议先掌握相关基础知识后再阅读本文,以便更好地理解内容。
15 1
|
26天前
|
存储 缓存 安全
只会“有序无序”?面试官嫌弃的List、Set、Map回答!
小米,一位热衷于技术分享的程序员,通过与朋友小林的对话,详细解析了Java面试中常见的List、Set、Map三者之间的区别,不仅涵盖了它们的基本特性,还深入探讨了各自的实现原理及应用场景,帮助面试者更好地准备相关问题。
56 20
|
22天前
|
算法 C语言 C++
【c++丨STL】list的使用
本文介绍了STL容器`list`的使用方法及其主要功能。`list`是一种双向链表结构,适用于频繁的插入和删除操作。文章详细讲解了`list`的构造函数、析构函数、赋值重载、迭代器、容量接口、元素访问接口、增删查改操作以及一些特有的操作接口如`splice`、`remove_if`、`unique`、`merge`、`sort`和`reverse`。通过示例代码,读者可以更好地理解如何使用这些接口。最后,作者总结了`list`的特点和适用场景,并预告了后续关于`list`模拟实现的文章。
42 7
|
2月前
|
存储 编译器 C语言
【c++丨STL】vector的使用
本文介绍了C++ STL中的`vector`容器,包括其基本概念、主要接口及其使用方法。`vector`是一种动态数组,能够根据需要自动调整大小,提供了丰富的操作接口,如增删查改等。文章详细解释了`vector`的构造函数、赋值运算符、容量接口、迭代器接口、元素访问接口以及一些常用的增删操作函数。最后,还展示了如何使用`vector`创建字符串数组,体现了`vector`在实际编程中的灵活性和实用性。
72 4
|
2月前
|
C语言 C++ 容器
【c++丨STL】string模拟实现(附源码)
本文详细介绍了如何模拟实现C++ STL中的`string`类,包括其构造函数、拷贝构造、赋值重载、析构函数等基本功能,以及字符串的插入、删除、查找、比较等操作。文章还展示了如何实现输入输出流操作符,使自定义的`string`类能够方便地与`cin`和`cout`配合使用。通过这些实现,读者不仅能加深对`string`类的理解,还能提升对C++编程技巧的掌握。
85 5
|
2月前
|
存储 算法 C++
【C++】unordered_map(set)
C++中的`unordered`容器(如`std::unordered_set`、`std::unordered_map`)基于哈希表实现,提供高效的查找、插入和删除操作。哈希表通过哈希函数将元素映射到特定的“桶”中,每个桶可存储一个或多个元素,以处理哈希冲突。主要组成部分包括哈希表、哈希函数、冲突处理机制、负载因子和再散列,以及迭代器。哈希函数用于计算元素的哈希值,冲突通过开链法解决,负载因子控制哈希表的扩展。迭代器支持遍历容器中的元素。`unordered_map`和`unordered_set`的插入、查找和删除操作在理想情况下时间复杂度为O(1),但在冲突较多时可能退化为O(n)。
28 5
|
2月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
65 2
|
25天前
|
存储 编译器 C语言
【c++丨STL】vector模拟实现
本文深入探讨了 `vector` 的底层实现原理,并尝试模拟实现其结构及常用接口。首先介绍了 `vector` 的底层是动态顺序表,使用三个迭代器(指针)来维护数组,分别为 `start`、`finish` 和 `end_of_storage`。接着详细讲解了如何实现 `vector` 的各种构造函数、析构函数、容量接口、迭代器接口、插入和删除操作等。最后提供了完整的模拟实现代码,帮助读者更好地理解和掌握 `vector` 的实现细节。
32 0
|
2月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
118 5