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

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

三、map

1.map特点

(1) map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。

(2)在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为pair:

typedef pair<const Key, T> value_type;

(3) 在内部,map中的元素总是按照键值key进行比较排序的。

(4) map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。

(5) map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。

(6)map通常被实现为平衡二叉搜索树(红黑树)。

2.map类

(1)map类的构造

1. template < class Key,                                     // map::key_type,Key的类型
2. class T,                                       // map::mapped_type,value的类型
3. class Compare = less<Key>,                     // map::key_compare,比较器类型,默认小于,自定义类型需写函数指针或仿函数
4. class Alloc = allocator<pair<const Key,T> >    // map::allocator_type 空间配置器
5.            > class map;
6. 
7. //构造map
8. explicit map (const key_compare& comp = key_compare(),
9. const allocator_type& alloc = allocator_type());
10. 
11. 使用迭代器区间构造map
12. template <class InputIterator>
13. map (InputIterator first, InputIterator last,
14. const key_compare& comp = key_compare(),
15. const allocator_type& alloc = allocator_type());
16. 
17. //拷贝构造map
18. map (const map& x);

创建map对象:

1. #define  _CRT_SECURE_NO_WARNINGS  1
2. #include<iostream>
3. #include<map>
4. using namespace std;
5. 
6. int main()
7. {
8.     map<string, int> m;//创建一个map对象m
9.  //向m中插入pair
10. 
11.   map<string, int> m1(m.begin(), m.end);//用m的区间构造m1
12.   map<string, int> m2(m1);//用m1拷贝构造m2
13. 
14.   return 0;
15. }

(2)插入insert( )

1. pair<iterator,bool> insert (const value_type& val);//当val的first已存在时返回值pair的second为false,否则返回pair的second为true
2. 
3. iterator insert (iterator position, const value_type& val);//在map的指定位置插入pair
4. 
5. template <class InputIterator>
6. void insert (InputIterator first, InputIterator last);//在map中插入一段迭代器区间

①插入pair,由于此时map中没有足球,因此pair的second为true:

1.  bool b = m.insert(pair<string, int>("足球", 2)).second;
2.  cout << b << endl;
3. 
4.  m.insert (pair<string, int>("篮球", 6));
5.  m.insert (pair<string, int>("羽毛球", 3));
6.  m.insert (pair<string, int>("排球", 1));

make_pair对pair进行了包装,构造了pair对象:

1. template <class T1,class T2>
2. pair<T1,T2> make_pair (T1 x, T2 y)
3.   {
4. return ( pair<T1,T2>(x,y) );
5.   }

因此,在项目中,不展开命名空间时,都要指定std,make_pair要比pair写起来更简洁一些,对比以下两种写法:

1.  m.insert(std::pair<std::string, int>("网球", 7));
2.  m.insert(std::make_pair("网球", 7));

明显make_pair的写法更简洁。

②在map的指定位置插入pair

1.  map<string, int> m;
2.  //向m中插入pair
3.  m.insert (pair<string, int>("足球", 2));
4.  m.insert (pair<string, int>("篮球", 6));
5.  m.insert (pair<string, int>("羽毛球", 3));
6.  m.insert (pair<string, int>("排球", 1));
7. 
8.  //在m开始插入乒乓球
9.  map<string, int>:: iterator it = m.begin();
10.   m.insert(it, pair<string, int>("乒乓球",5));

监视:

③在map中插入一段迭代器区间

向m1中插入m从第二个位置开始到结束位置的区间:

1.  map<string, int> m1;
2.  m1.insert(++m.begin(), m.end());

监视:

这里少了篮球,说明m的第一个pair是<"篮球",6>。

(3)map遍历

auto在变量被定义并初始化之后编译器才能根据初始化表达式来推导auto的实际类型,所以在定义map对象时,不能使用auto关键字,在变量被定义并初始化之后可以使用auto关键字:

1.  map<string, int> m;
2. 
3.  m.insert(pair<string, int>("足球", 2));
4.  m.insert(pair<string, int>("篮球", 6));
5.  m.insert(pair<string, int>("羽毛球", 3));
6.  m.insert(pair<string, int>("排球", 1));
7.  m.insert(make_pair("网球", 7));
8. 
9. //map<string, int>::iterator mit = m.begin();
10.   auto mit = m.begin();//同map<string, int>::iterator mit = m.begin();
11.   while (mit != m.end())
12.   {
13.     cout << mit->first << ":" << mit->second<< endl;
14.     mit++;
15.   }
16.   cout << endl;

map本身有两个模板参数,会导致有些类型比较长,项目中可以用typedef简化命名:

1.  typedef std::map<std::string, int> MAP;//简化map命名
2.  typedef std::pair<std::string, int> MAP_KV;//简化pair命名
3.  typedef std::map<std::string, int>::iterator MAP_IT;//简化迭代器命名
4. 
5.  MAP m;
6.  m.insert(MAP_KV("足球", 2));
7.  m.insert(MAP_KV("篮球", 6));
8.  m.insert(MAP_KV("羽毛球", 3));
9.  m.insert(MAP_KV("排球", 1));
10.   m.insert(MAP_KV("网球", 7));
11. 
12.     MAP_IT mit = m.begin();
13.   while (mit != m.end())
14.   {
15.     std::cout << mit->first << ":" << mit->second << std::endl;
16.     mit++;
17.   }
18.   std::cout << std::endl;

同set一样,map的key不允许修改

1.  typedef std::map<std::string, std::string> MAP;//简化map命名
2.  typedef std::pair<std::string, std::string> MAP_KV;//简化pair命名
3.  typedef std::map<std::string, std::string>::iterator MAP_IT;//简化迭代器命名
4. 
5.  MAP m;
6.  m.insert(MAP_KV("spring", "春天"));
7.  m.insert(MAP_KV("summer", "夏天"));
8.  m.insert(MAP_KV("autumn", "秋天"));
9.  m.insert(MAP_KV("winter", "冬天"));
10. 
11.   MAP_IT mit = m.begin();
12.   while (mit != m.end())
13.   {
14.     mit->first = "night";//修改key为night,报错
15.     mit++;
16.   }
17.   std::cout << std::endl;

报错:

但是map的value可以修改

先给中文翻译加上{ }

1.  MAP_IT mit = m.begin();
2.  while (mit != m.end())
3.  {
4.    mit->second.insert(0, "{");
5.    mit->second += "}";
6.    std::cout << mit->first << ":" << mit->second << std::endl;
7.    mit++;
8.  }
9.  std::cout << std::endl;

再给spring的中文翻译加上其他释义:

1.  auto ret = m.find("spring");
2. 
3.  mit = m.begin();
4.  if(ret != m.end())
5.  {
6.    std::string& str = ret->second;//用str作为ret->second的别名,因此控制的就是ret的second
7.    str.insert(str.size() - 1, "、温泉");
8.  }
9. 
10.   while (mit != m.end())
11.   {
12.     std::cout << mit->first << ":" << mit->second << std::endl;
13.     mit++;
14.   }
15.   std::cout << std::endl;

map也可以用来统计次数,比如找出大家最喜欢的球类,统计技术有3种方式:

第一种方式:找到就增加次数,否则就插入

1. //1.统计次数   
2.  string arr[] = { "篮球","足球","排球","羽毛球","足球","乒乓球","足球","排球","羽毛球","篮球","足球" };
3.  map<string, int> countMap;
4.  for (const auto& str : arr)
5.  {
6.    map<string, int>::iterator ret = countMap.find(str);
7.    if (ret != countMap.end())//找到了,只增加次数
8.    {
9.      ret->second++;
10.     }
11.     else//没找到就插入
12.     {
13.       countMap.insert(make_pair(str, 1));
14.     }
15.   }
16. 
17.   //2.找出大家最喜欢的球类
18.   for (const auto& e : countMap)
19.   {
20.     cout << e.first << ":" << e.second << endl;
21.   }

第二种方式:由于insert的返回值类型为pair<iterator, bool>,pair中的第二个值类型为bool,向map中插入时,如果map中已经有了,pair返回的第一个值为插入值所在节点的迭代器,第二个值就为false,否则pair返回的第一个值为插入值所在节点的迭代器,第二个值就为true。

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

因此第二种方式根据插入的返回值pair的第二个值判断为true还是false,决定返回的pair的第一个值即迭代器的第二个值是否++:

1.  string arr[] = { "篮球","足球","排球","羽毛球","足球","乒乓球","足球","排球","羽毛球","篮球","足球" };
2.  map<string, int> countMap;
3. 
4. //先插入,如果str已经在map中,insert会返回str所在节点的迭代器,++次数即可
5.  for (const auto& str : arr)
6.  {
7.    //pair<map<string,int>::iterator,bool> ret = countMap.insert(make_pair(str, 1));//可用auto推导
8.    auto ret = countMap.insert(make_pair(str, 1));
9.    if (ret.second == false)
10.     {
11.       ret.first->second++;
12.     }
13.   }
14. 
15.   for (const auto& e : countMap)
16.   {
17.     cout << e.first << ":" << e.second << endl;
18.   }


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