【11】C++ 关联容器之map与set


关联容器支持高效地关键字查找和访问。两个主要地关联容器(associative-container)类型是mapset

map中的元素是一些关键字-值(key-value)对关键字起到索引的作用,值则表示与索引相关联的数据。

set中每个元素包含一个关键字;set支持高效地关键值查询操作 -- 检查一个给定关键字是否在set中。

类型mapmultimap定义在头文件map中;setmultiset定义在头文件set中;

无序容器则定义在头文件unordered_mapunordered_set

关联容器类型
按关键字有序保存元素(有序容器)
map关联数组:保存关键字-值对
set 关键字即值,即只保存关键字的容器
multimap关键字可重复出现的map
multiset关键字可重复出现的set 
无序容器
unordered_map用哈希函数组织的map
unordered_set用哈希函数组织的set
unordered_multimap哈希组织的map;关键字可以重复出现
unordered_multiset哈希组织的set;关键字可以重复出现

map关键字-值对组合,map通常被称为关联数组。

例如:单次计数

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

int main()
{
    map<string,size_t> word_count;
    string word;
    while(cin >> word){
        ++word_count[word];
    }
    for(auto i : word_count)
        cout << i.first << " occur " << i.second << " times" << endl;
    cout << "Hello World!" << endl;
    return 0;
}

1、定义关联容器

定义一个map时,必须指明关键字类型值类型

map<string ,size_t> word_count;    //空容器

map<string ,string> authorInfo = {{"phone","150XXXX9410"},{"name","utotao"}}

当初始化一个map时,必须提供关键字类型和值类型,将每一个关键值-对包围在花括号中:

{key , value}

来指出它们一起构成了map中的一个元素。在每个花括号中,关键值是第一个元素,值是第二个。

 

2、pair对

(1)头文件:

#include <utility>

(2)pair创建及初始化

当创建pair的时候,必须提供两个类型名

#include <utility>
#include <vector>

pair<string, string> anon{"name","utotao"};//初始化
pair<string,size_t> anon2;
pair<string,vector<string >> anon3;

(3)pair上的操作:

pair<T1, T2> pp是一个pair,两个类型分别是T1和T2的成员都进行了初始化
pair<T1, T2> p(v1, v2)p是一个成员类型为T1,T2的pair;first和second成员分别用v1和v2进行初始化
pair<T1, T2> p = {v1, v2}等价于pair<T1, T2> p(v1, v2)
make_pair(v1,v2)返回一个用v1,v2初始化的pair。pair的类型是由v1和v2的类型推导而来的
p.first返回p的名为first(公有)数据成员
p.second返回p的名为second(公有)数据成员
p1 relop p2关系运算符(>,>=,<,<=)按字典序定义
p1 == p2当first和second成员分别都相等的时候,p1等于p2;否则,不相等
p1 != p2

3、关联容器操作

(1) 关联容器额外的类型别名

key_type此容器类型的关键字类型
mapped_type每个关键字关联的类型:只适用于map
value_type

对于set,与key_type相同

对于map,为pair<const key_type , map_type>

对于set类型,key_typevalue_type是一样的;set中保存的值就是关键字。

map中,元素是关键字-值对。即,每个元素是一个pair对象,包含一个关键字和一个关联的值。由于不能改变这些关键字,因此这些pair的关键字部分是const的。

(2) 关联容器迭代器

对于map来说,value_type是一个pair对,其first成员保存const关键字,second保存成员值。

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

int main()
{
    map<string,size_t> word_count;
    string word;
    while(cin >> word){
        ++word_count[word];
    }
    for(auto i : word_count)
        cout << i.first << " occur " << i.second << " times" << endl;

    cout << "==============" << endl;
    auto map_it = word_count.begin();
    cout << (*map_it).first << endl;
    cout << (*map_it).second << endl;
    return 0;
}

(3) 添加元素

向set中添加元素:

vector<int > ivec = {2,4,6,8,2,4,6,8};
set<int > set2;
set2.insert(ivec.cbegin(),ivec.cend());
set2.insert({1,3,5,7,1,3,5,7});

向map中添加元素:

//向word_count插入word的4种方法
word_count.insert({word,1});
word_count.insert(make_pair(word,1));
word_count.insert(pair<string,size_t>(word,1));
word_count.insert(map<string,size_t>::value_type(word,1));

在新标准下,创建一个pair最简单的方法就是在参数列表中使用花括号初始化,也可以调用make_pair或显式构造pair。

c.insert(v)

v是value_type类型的对象;args用来构造一个元素

c.emplace(args)对于map和set,只有当元素的关键字不在c中时才插入(或构造)元素。函数返回一个pair,包含一个迭代器,指向具有指定关键字的元素,以及一个指示插入是否成功的bool值。
c.insert(b ,e)

b和e是迭代器,表示一个c::value_type类型值的范围;i1是这种值的花括号列表。函数返回void

对于map和set,只插入关键字不在c中的元素。对于multimap和multiset,则会插入范围中的每个元素

c.insert(i1)
c.insert(p , v)类似insert(v)(或emplace(args)),但迭代器p作为一个提示,指出从哪里开始搜索新元素应该存储的位置。返回一个迭代器,指向具有给定关键字的元素
c.emplace(p, args)

insert返回值:对于不包含重复关键字的容器,添加单一元素的insertemplace版本返回一个pairpair的第一个元素first成员是一个迭代器,指向具有给定关键字的元素;second成员是一个bool,指出元素是插入成功还是已经存在于容器中。如果关键字已经存在于容器之中,则insert什么事情也不做,且返回值中的bool部分为false。如果关键值不存在,元素被插入容器中,且bool值为true

#include <iostream>
#include <map>
#include <string>

using namespace std;

int main()
{
    string word;
    map<string,size_t> word_count;
    while(cin >> word){
        auto ret = word_count.insert({word,1});
        if(!ret.second)
            ++ret.first->second;
    }
    for(auto i : word_count)
        cout << i.first << " " << i.second << endl;
    cout << "Hello World!" << endl;
    return 0;
}

if语句检查返回值的bool部分,若为false,则表明插入未发生。在此情况下,word已经存在于word_count之中,因此必须递增此元素所关联的计数器。

(4) 删除元素

关联容器顶一个三个版本的erase操作函数:

  1. 传入一个迭代器,删除指定的元素
  2. 传入一个迭代器范围,删除指定范围的元素
  3. 接受一个key_value参数,删除所匹配给定关键字的元素(如果存在的话),返回实际删除元素的数量
c.erase(k)从c中删除每一个关键字为k的元素。返回一个size_type值,指出删除的元素的数量
c.erase(p)从c中删除迭代器p指定的元素。p必须指向c中一个真实元素,不能等于c.end()。返回一个指向p之后元素的迭代器,若p指向c中的尾元素,则返回c.end()
c.erase(b,e)删除迭代器对b和e所表示的范围中的元素。返回e

注意:当解引用一个关联容器迭代器的时候,所得到的是一个类型为容器的value_type的值的引用。

例子:删除关键字为hello的元素

#include <iostream>
#include <map>
#include <string>

using namespace std;

int main()
{
    string word;
    map<string,size_t> word_count;
    while(cin >> word){
        auto ret = word_count.insert({word,1});
        if(!ret.second)
            ++ret.first->second;
    }
    cout << "==============" << endl;
    for(auto i : word_count)
        cout << i.first << " " << i.second << endl;
    word_count.erase("hello");
    cout << "===== after erase hello =====" << endl;
    for(auto i : word_count)
        cout << i.first << " " << i.second << endl;
    cout << "===== after erase iterate =====" << endl;
    word_count.erase(word_count.cbegin(),prev(word_count.cend()));
    for(auto i : word_count)
        cout << i.first << " " << i.second << endl;

    return 0;
}

(5) map下标操作访问元素

① map下标操作:

map和unordered_map提供了下表操作,set没有提供下表操作,因为set没有与关键字(key)相对应的值。不能对multimap和unordered_multimap进行下标操作,因为这两种容器可能有多个值与通过一个关键字相关联。

对map使用下标操作(索引为关键字):

case 1 : 容器中有对应的关键字,返回与之相关联的值

case 2 : 容器中没有该关键字,创建该元素并插入到map中,关联值将进行值的初始化

map和unordered_map使用下标操作
c[k]返回关键字为k的元素;如果k不在c中,添加一个关键字为k的元素,对其进行值的初始化
c.at[k]访问关键字为k的元素,带参数检查;若k不在c中,则抛出一个out_of_rage异常

② 访问元素:

在一个关联容器中查找元素的方法

lower_bound和upper_bound不适用于无序容器

下标和at操作只适用于非const的map和unordered_map

c.find(k)返回一个迭代器,指向第一个关键字为k的元素;若k不在容器中,则返回尾后迭代器
c.count(k)返回关键字为k的元素数量。对于不允许重复关键字的容器,返回值永远是0或者1
c.lower_bound(k)返回一个迭代器,指向第一个关键字不小于k的元素
c.upper_bound(k)返回一个迭代器,指向第一个关键字大于k的元素
c.equal_range(k)

返回一个迭代器pair,表示关键字等于k的元素范围。若k不存在,pair的两个成员均等于c.end()


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值