C++复习(九):STL库之map、multimap、unordered_map、unordered_multimap

8 篇文章 0 订阅

map和set差不多,底层结构也是红黑树,unordered版本底层也是hash表,主要区别是和键值对key和value相关的东西。

这篇文章主要讨论map的用法,另外三个容器和map的用法区别与set和与其对应的三个容器的区别一样。

map的元素类型为value_type=pair<const key_type,mapped_type>,这个是官网的说法,在本文里为了让它和键值对的叫法对应起来,我给它改个名字pair<key_type, value_type>,对pair内元素的访问用first和second来访问。

1、构造和拷贝构造(这些容器一般不赋初值)

#include<iostream>
#include<map>    //包含map和multimap
#include<unordered_map>    //包含unordered_map和unordered_multimap
using namespace std;
 
//以下各种构造只拿map举例。
//普通构造
map<char, int> m1;
 
//拷贝构造
map<char, int> m2(m1);


auto cmp1 = [](char a, char b) {return (a > b); };    //cmp1为比较器。若返回值为true,则a排b前面,反之排后面。
//也可以写成下面的形式,不过调用的时候有不一样。
auto cmp2(char a, char b) 
{
    return (a > b);
}

//其它构造
map<char, int> m3(m1.begin(),m1.end());    //两个参数,分别为迭代器。用m1的部分值生成。
map<char, int, decltype(cmp1)> m4(cmp1);    //用来产生一个以cmp1为排序准则的map。
map<char, int, decltype(cmp1)> m5(m1.begin(),m1.end(),cmp1);    //三个参数,将m1的部分值按cmp为排序准则生成m5。
//调用cmp2的方式
map<char, int, decltype(&cmp2)> m5(m1.begin(),m1.end(),&cmp2);

2、大小和是否为空

//仅拿map举例
m1.size()    //返回m1的大小
m1.empty()    //若m1为空则返回true,否则返回false。

3、插入、删除、查找和计数

#include<vector>
 
//插入,仅以map举例
//插入一个数,法一:
/*
返回pair<iterator,bool>,前一个为迭代器——指向新插入的元素或原有的该元素,后一个为bool——表示是否插入成功。
pair的两个元素分别用.first和.second来访问。
*/
m1.insert(pair<char,int>('a',2));    //将{'a',2}插入m1中。m1中值为{'a':2}。若关键词不存在则插入,返回值.second为true;若关键词已存在则不插入,返回值.second为false。

//插入一个数,法二:
//返回值和法一一样。这个方法插入应该是最简单的插入方法了。
m1.emplace('f',7);    //将{'e',7}插入m1中。m1中值为{'a':2, 'f':7}。

//插入一个数,法三:
//这个其实不能算插入方法,除非能保证关键词在map中不存在。同时multimap不支持这样访问关键词,因为multimap中一个关键词可能有多个值。
m1['d']=6;    //m1中值为{'a':2, 'd':6, 'f':7}。若关键词不存在则插入,若关键词已存在则改变该关键词的值。

//插入多个数
//无返回值。
vector<pair<char,int>> v={pair<char,int>('b',1), pair<char,int>('c',4)};
m1.insert(v.begin(), v.end());    //将区间内的数全都插入m1中。m1中值为{'a':2, 'b':1, 'c':4, 'd':6, 'f':7}。若关键词不存在则插入,若关键词已存在则不插入。


//下面这个一般也用不到
//返回一个迭代器——指向新插入的元素或原有的该元素。
m1.insert(m1.begin(),pair<char,int>('e',5));    //将{'e',5}插入m1中,第一个参数为迭代器作为搜索起点,提升插入速度。m1中值为{'a':2, 'b':1, 'c':4, 'd':6, 'e':5, 'f':7}。
 
//删除
m1.erase('e');    //删除key为'e'的元素。m1中值为{'a':2, 'b':1, 'c':4, 'd':6, 'f':7}。
m1.erase(m1.begin());    //删除迭代器指向的元素。m1中值为{'b':1, 'c':4, 'd':6, 'f':7}。
m1.erase(m1.begin(),m1.end());    //删除区间中元素,可以配合find使用。m1中值为{}。返回删除的元素个数。
m1.clear();    //移除全部元素,清空容器。
 
//查找。设此时m1值为{'a':2, 'b':1, 'c':4, 'd':6, 'e':5, 'f':7}
//对于multimap,每次调用得到的返回值->second可能为不同的值,因为同一个关键词可能对应了很多值。
m1.find('c');    //查找key为'c'的元素。若找到则返回该元素对应迭代器,找不到则返回m1.end()。访问返回值->second可以获得该关键词的值;返回值->first应该为该关键词,没有访问的必要。
m1['c']    //在知道关键词必定存在时,直接访问关键词的值。multimap不能这么访问。
if(m1.count(c))    //表示c在m1中

//计数。可以把计数用作查找,比如if(m1.count(c))表示c在m1中
m1.count('c');    //查找key为'c'的元素的个数。对于map只会返回0或1,对于multimap则返回元素的个数,可以为多个。

 

4、map的遍历

下述代码来源:https://www.cnblogs.com/silentteller/p/10184641.html

#include <iostream>
#include <map>
using namespace std;

int main(){
    map<int,int> m;
    for (int i = 0; i < 10; i++){
        m[i] = i*i;
    }
    map<int,int>::iterator iter;
    iter = m.begin();
    while(iter != m.end()){
        cout << iter->first << "-" << iter->second << endl;
        iter++;
    }
    for (iter = m.begin();iter != m.end(); iter++){
        cout << iter->first << "-" << iter->second << endl;
    }
    for(auto &it : m){
        cout << it.first << "-" << it.second <<endl;
    }
    return 0;
}

5、map对value排序

 把map中的元素放到序列容器(如vector)中,再用sort进行排序。

map<string,int> m100;
vector<pair<string, int>> vec(m100.begin(), m100.end());
sort(vec.begin(), vec.end(), cmp);    //cmp可以自己定义

6、关于元素类型为结构体或类的unordered_map和unordered_multimap的使用

当key类型为结构体或类时,由于使用了hash表的结构,必须要为该结构体或类重载“==”符号和写上对应hash函数的结构体。

具体方法见https://blog.csdn.net/zhangxiao93/article/details/73741460

 

7、关于multimap的其他说明

以下内容摘抄自http://c.biancheng.net/view/518.html

multimap 不支持下标运算符,因为键并不能确定一个唯一元素。和 map 相似,multimap 也不能使用 at() 函数。multimap 的成员函数 fmd() 可以返回一个键和参数匹配的元素的迭代器。例如:

std::multimap<std::string, size_t> people {{"Ann",25},{"Bill", 46}, {"Jack", 77}, {"Jack", 32},{"Jill", 32}, {"Ann", 35} };
std::string name {"Bill"};
auto iter = people.find(name);
if (iter ! = std::end (people))
    std::cout << name << " is " << iter->second << std::endl;
iter = people.find ("Ann");
if (iter != std::end(people))
    std::cout << iter->first << " is " << iter->second <<std::endl;

如果没有找到键,会返回一个结束迭代器,所以我们应该总是对返回值进行检查。第一个 find() 调用的参数是一个键对象,因为这个键是存在的,所以输出语句可以执行。第二个 find() 调用的参数是一个字符串常量,它说明参数不需要和键是相同的类型。对容器来说,可以用任何值或对象作为参数,只要可以用函数对象将它们和键进行比较。最后一条输出语句也可以执行,因为有等于 "Ann" 的键。事实上,这里有两个等于 "Ann" 的键,你可能也会得到不同的运行结果。

如果使用 multimap 容器,几乎可以肯定它会包含键重复的元素;否则,就应该使用 map。一般来说,我们想访问给定键对应的所有元素。成员函数 equal_range() 就可以做到这一点。它会返回一个封装了两个迭代器的 pair 对象,这两个迭代器所确定范围内的元素的键和参数值相等。例如:

auto pr = people.equal_range("Ann");
if(pr.first != std::end(people))
{
    for (auto iter = pr.first ; iter != pr.second; ++iter)
    std:cout << iter->first << " is " << iter->second << std::endl;
}

equal_range() 的参数可以是和键同类型的对象,或是不同类型的但可以和键比较的对象。返回的 pair 对象的成员变量 first 是一个迭代器,它指向第一个大于等于参数的元素;如果键和参数相等的元素存在的话,它是第一个键和参数相同的元素。如果键不存在,pair 的成员变量 first 就是容器的结束迭代器,所以应该总是对它们进行捡查。

pair 的成员变量 second 也是一个迭代器,它指向键值大于参数的第一个参数;如果没有这样的元素,它会是一个结束迭代器。这段代码会输出容器中键值为”Ann”的元素的一些信息。

multimap 的成员函数 lower_bound() 会返回一个迭代器,它指向键值和参数相等或大于参数的第一个元素,或者指向结束迭代器。upper_bound() 也返回一个迭代器,它指向键值大于函数参数的第一个元素,如果这样的元素不出现的话,它就是一个结束迭代器。所以,当存在一个或多个相等键时,这些函数会返回一个开始迭代器和一个结束迭代器,它们指定了和参数匹配的元素的范围,这和 equal_range() 返回的迭代器是相同的。因而前面的代码段可以这样重写:

auto iter1 = people.lower_bound("Ann");
auto iter2 = people.lower_bound("Ann");
if(iter1 != std::end(people))
{
    for(auto iter = iterl ; iter != iter2; ++iter)
    std::cout << iter->first << " is " << iter->second << std::endl;
}

它和前一个代码段的输出结果是相同的。通过调用 multimap 的成员函数 count() 可以知道有多少个元素的键和给定的键相同。

auto n = people.count("Jack"); // Returns 2

可以用不同的方式使用这些函数。可以选择 find() 或 equal_range() 来访问元素。如果以班级为键,在 mutilmap 中保存学生信息,可以用成员函数 count() 来获取班级的大小。当然,通过将在前面章节介绍的 distance() 函数模板运用到成员函数 equal_range() 返回的迭代器或者 lower_bound() 和 upper_bound() 返回的迭代器上,也可以获取键和给定键相等的元素 的个数:

std::string key{"Jack"};
auto n = std::distance( people.lower_bound(key),people.upper_bound(key)); // No. of elements matching key

注意,全局的 equal_range()、lower_bound()、upper_bound() 函数模板的使用方式和关联容器中同名成员函数的使用方式略有不同。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值