C++ Primer笔记11---chapter11 关联容器
Posted Ston.V
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ Primer笔记11---chapter11 关联容器相关的知识,希望对你有一定的参考价值。
1.关联容器:
关联容器支持高效的关键字查找和访问,主要是map和set,然后各自有multi、unordered及unordered_multi三个版本,也就是共八种容器。
map是键值对的集合,如map[xxx],如果map中没有xxx会直接创建,这与vector不同;
set是关键字的简单集合,当只想知道一个值是否存在时,set最有用(成员函数find,若找到返回对应迭代器,没找有返回尾后迭代器)
按关键字有序保存元素,头文件set 、map map 关联数组:<关键字,值> set 关键字即值,即只保存关键字的容器 multimap 关键字可重复出现的map multiset 关键字可重复出现的set
无序集合,头文件unordered_set、unordered_map unordered_map 用哈希函数组织的map unordered_set 用哈希函数组织的set unordered_multimap 哈希组织的map,关键组可重复出现 unordered_multiset 哈希组织的set,关键组可重复出现
2.关键字类型要求
对于有序集合,如果一个类型定义了'<'运算符,则他可以作为关键字类型
2.1 自定义类型做关键字
为了使用自己定义的操作,在定义multiset时我们必须提供两个类型:关键字类型以及比较操作类型(应该是一种函数指针类型)
bool compareIsbn(const Sales_data *lhs,const Sales_data *rhs) { return lhs.isbn() < rhs.isbn; } //定义以自定义类型Sales_data为关键字的multiset //使用decltype(compareIsbn)*这个函数指针类型指出操作,用conpareIsbn初始化对象,这表示我们向bookstore添加元素时,通过调用compareIsbn来为这些元素排序,原本构造函数的参数应该用&compareIsbn,但是这里函数类型可以自动转换函数指针类型(尖括号部分的不行,必须加上*号,表示为函数指针) multiset <Sales_data, decltype(compareIsbn)*> bookstore(compareIsbn);
2.2 pair类型
定义在头文件utility中
如果有一个函数需要返回一个pair,我们可以对返回值进行列表初始化(直接返回一个花括号),但是较早版本中不可以,必须显示的构造一个pair对象
要构造一个空的pair,可以直接隐式构造,会有默认值的 //pair<string, int>();
3.关联容器操作
每个关联容器都有三种类型
key_type 此容器类型的关键字类型 mapped_type 每个关键字关联的类型,只适用于map value_type 对于set,与key_type相同;对于map,为pair<const key_type, mapped_type>
3.1 关联容器迭代器
map的迭代器解引用为value_type类型,是一个pair,注意其中的关键字是const,不可改变
set的迭代器是const的,只能够读访问set中的元素(set的关键字也是const的)
3.2 关联容器和算法
通常不对关联容器使用泛型算法。原因就是const这一特性意味着不能将关联容器传递给修改或者重排容器元素的算法,因为这类算法需要向元素写入值,而set类型中的元素是const的,map中的元素是pair,其first成员是const的。
关联容器可用于只读取的算法,但是泛型算法往往会顺序搜索,使用成员函数往往快的多(比如find)
在实际编程中,如果要对一个关联容器使用算法,一般都是将他当做原序列或者目的位置,比如使用copy拷贝
3.3 添加元素
set的insert接受一对迭代器或者一个初始化列表
map的insert参数是pair,可以用pair的构造函数形式构造出一个临时pair,也可以直接用花括号,还可以使用make_pair函数构造一个临时pair。(这里实际上不就是构造函数的类型转换嘛)
insert的返回值是一个pair,first成员是一个指向具有给定关键字元素的迭代器(一个指向pair的迭代器),second成员是一个bool变量表示是否插入成功
对于multimap的inset不需要返回值,总能插入成功
举一个统计单词数目的例子
//统计单词数目 map<string, int>word_cnt; string word; while(cin>>word){ //这里map中,如果没有关键字,会自动将键值设置为0 ++word_cnt[word]; } for(const auto &w:word_cnt) cout<< w.first << " occurs " << w.second << ((w.second > 1) ? "times" : "time" <<endl; //统计单词数目,如果用insert的方法 map<string, int>word_cnt; string word; while(cin>>word){ auto ret=word_cnt.insert((word,1)); //通过返回值判断是否插入成功,即当前是否已有相同关键字 if(!ret.second) ++ret.first->second; //这句话你自己好好琢磨 }
3.4 删除元素
使用erase,返回值为删除元素的个数。对于没有指定元素的,返回0
3.5 下标操作
set不支持下标运算(存的就是关键字)
对于map,使用下标操作,其行为与数组或vector上使用下标操作很不同:使用一个不在容器中的关键字作为下标,会添加一个具有此关键字的元素到map中
map的下标运算返回的是mapped_type类型(键值对中的值),而其迭代器解引用得到的是value_type(pair类型),这两不一样。(vector和string的这俩操作得到的类型一样)
3.6 访问元素
c.find(k) 返回指向包含关键字k的元素的迭代器,如不存在,返回尾后迭代器 c.count(k) 返回等于关键字等于k的元素数量 c.lower_bound(k) 返回一个迭代器,指向第一个关键字不小于k的元素 c.upper_bound(k) 返回一个迭代器,指向第一个关键字大于k的元素 c.equal_range(k) 返回一个迭代器pair,表示迭代器范围。若不存在,则两个成员均为尾后迭代器
3.7 multimap或multiset中查找元素的三种方法
特征:在有序的容器中,对关键字相同的元素的都是放在一起的
使用count和find函数的组合
string search_item("Alain de Botton"); auto entries = authors.count(search_item); //元素的数量 auto it = authors.find(search_item); //返回第一个指向元素的迭代器 while(entries){ cout<< it->second <<endl; ++it; --entries; }
使用面向迭代器的方法解决
//lower_bound返回第一个指向给定元素的迭代器(若没有则指向比给定元素大的元素),up_bound返回第一个比给定元素大的元素 for(auto beg = authors.lower_bound(search_item), end = authors.up_bound(search_item); beg!=end;++beg) cout<< beg->second <<endl;
使用euqal_range函数(返回一个pair,其first和second构成一个迭代器范围)
for(auto pos=authors.equal_range(search_item); pos.first!=pos.second;++pos.first) cout << pos.first->second <<endl;
4.无序容器
前文中有序关联容器使用“<”来组织元素(红黑树),无序关联容器使用哈希函数和“=”运算符来组织容器
无序容器的性能依赖于哈希函数的质量和桶的数量的大小
无序容器对关键字类型的要求
无序容器使用关键字类型的==运算符Laura比较元素。我们不能直接定义关键字类型为自定义类型的无序容器,而必须提供我们自己的hash函数模板
size_t hasher(const Sales_data &sd){ return hash<string>()(sd.isbn()); } bool eqOp(const Sales_data &lhs,const Sales_data &rhs){ return lhs.isbn() == rhs.isbn(); } using SD_multiset = unordered_multi<Sales_data, decltype(hasher)*, decltype(eqOp)*>; //参数是:桶大小,哈希函数指针,相等性判断运算符函数指针 SD_multiset bookstore(42,hasher,eqOp); //如果我们的类定义了==运算符,则可以只重载哈希函数 unordered_multiset<Foo, decltype(FooHash)*> fooSet(10,FooHash)
以上是关于C++ Primer笔记11---chapter11 关联容器的主要内容,如果未能解决你的问题,请参考以下文章
C++ Primer笔记16---chapter13 代码实例
C++ Primer笔记15---chapter13 拷贝控制2