目录
1.5.5 下标操作(非const的map、multimap)
1. 有序关联容器
1.1 有序关联容器要点
1.容器里的元素对都是有序的,并且是按关键字来排序的。
2.容器里元素的排序,是按"<"运算符来算的,所以容器的关键字的类型必须支持"<"运算符
1.2 分类:
容器 | 头文件 | 含义 | 底层实现 |
map <key_type,mapped_type> | #include<map> | 关联数组,保存关键字-值对,关键字唯一 | 红黑树 |
multimap <key_type,mapped_type> | #include<map> | 关键字可重复的map,关键字对应多个值 | 红黑树 |
set <key_type> | #include<set> | 关键字即值,即只保存关键字的容器,关键字唯一 | 红黑树 |
multiset <key_type> | #include<set> | 关键字可重复的set | 红黑树 |
pair <key_type,mapped_type> | #include<utility> | 是一个用来生成特定类型的模板,map中每一个元素对,都是pair类型。 | 无 |
1.3 关键字类型要求
1.有序容器的关键字必须遵循严格弱序(C++ 严格弱序)的要求,即必须有'<'运算符的定义
2.有序容器set可以指定操作类型,使用关键字类型的比较函数,其函数传入的是一个函数指针,形式:set<key_type, 函数指针> / set<key_type,decltype(函数) * >,并且函数指针指向的自定义比较函数,也必须遵循严格弱序的要求。
1.4 pair类型
头文件:#include<utility>
形式:pair <key_type,mapped_type>
含义:是一个用来生成特定类型的模板,并且pair的数据成员都是公有的,map中每一个元素对,都是pair类型。
操作 | 含义 |
pair<T1,T2> p; | 创建一个名为p的pair类型,并对T1,T2类型进行值初始化 |
pair<T1,T2> p(v1,v2); pair<T1,T2> p={v1,v2}; | 创建一个名为p的pair类型,并对T1,T2类型分别用v1,v2进行初始化 |
make_pair(v1,v2); | 返回一个v1,v2初始化的pair。pair的对应类型由v1,v2推断而出。 |
p.first; | 返回p的名为first的公有数据成员 |
p.second; | 返回p的名为second的公有数据成员 |
p1 relop p2 | relop是关系运算,关系运算用运算符'<'来实现 |
p1==p2 p1!=p2 | 当first和seconde都相等时,才相等,否则不等 |
1.5 关联容器操作
1.5.1 类型别名
关联容器额外的类型别名 | 含义 | 操作(map为例) |
key_type | 此容器的关键字类型 | map<string,int>::key_type |
mapped_type | map容器的值类型,只有map才有 | map<string,int>::mapped_type |
value_type | map:pair<const key_type,mapped_type> set:与key_type相同 | map<string,int>::value_type |
1.5.2 迭代器
操作 | 注意 |
解引用符:*mapiterator,*setiterator | 1.对map迭代器使用解引用符,会得到一个value_type类型的值,即pair<const key_type,mapped_type>,注意这里的关键字是const类型。 2.对set迭代器使用解引用符,会得到一个const key_type类型,这个关键字也是const类型。 |
遍历关联容器 | 关联容器支持begin()和end()操作,可以用来遍历容器,但是迭代器在遍历容器时是按关键字的升序来遍历的。 |
关联容器和算法 | 一般来说关联容器很少使用算法,因为关联容器的迭代器取到的是const类型,所以像修改、排序等算法就不能使用,但是关联容器可以用作目的位置和源序列,比如:copy(map.begin(),map.end(),back_inserter(vector)); |
1.5.3 添加
对于map和set来说,只有当要插入的关键字不存在容器里时,才进行插入。
添加元素 | 含义 |
c.insert(v); c.emplace(args); | v是value_type类型,args是构造生成的单个元素。 函数返回一个pair,pair的first是一个迭代器指向插入的元素的位置,second是一个bool类型。second为true意味着插入成功,false意味着容器里已经存在要插入的这个关键字了,并且first迭代器指向这个已存在的关键字的位置。 对于multimap和multiset来说,元素的插入总是成功的,函数返回一个指向新元素的迭代器 |
c.insert(b,e); c.insert(il); | b和e是一对迭代器,表示一个c::value_type(pair<......>)类型值的范围,il是c::value_type的花括号列表。 函数返回void。 |
c.insert(p,v); c.emplace(p,args); | 类似insert(v),p是一个迭代器,指出应该从p所指向的元素位置开始搜索新元素应该存储的位置。 返回一个迭代器,指向具有给定关键字的元素。 |
1.5.4 删除
删除元素 | 含义 |
c.erase(k); | k是一个关键字类型的元素,从c中删除每个关键字为k的元素。 返回一个size_type值,指出删除元素的数量 |
c.erase(p); | p是一个迭代器,p必须指向c中容器的某个元素,且不能为c.end(),从c中删除迭代器p指向的元素。 返回一个指向p之后的元素的迭代器。 |
c.erase(b,e); | b,e是一对迭代器,删除b和e所指范围内的元素,返回e |
1.5.5 下标操作(非const的map、multimap)
set不支持此操作:因为set只有关键字没有关联的值。
map的下标操作——map[key_type]=value流程:
Step1:在map里查找关键字为key_type的值
Step2:如果找到,则将其值赋为value
Step3:如果没找到,则将关键字key_type插入到map容器里
Step4:新插入的关键字的值进行值初始化
Step5:将value赋值给关键字key_type
因为map的下标操作会对新关键字进行插入,所以const类型的map不能使用下标操作。
map[]与*(map<...>::iterator)——map下标操作与map迭代器解引用操作的区别:
map下标操作返回的是mapped_type对象。
map迭代器解引用操作返回的是value_type对象。
1.5.6 访问
查找元素 | 含义 |
lower_bound、upper_bound不使用于无序容器 | 下标和at操作只适用用于非const的map和unordered_map |
c.find(k); | 返回一个迭代器,指向第一个关键字为k的元素,如果k不在迭代器中,则返回end(); |
c.count(k); | 返回关键字为k的元素的数量。不重复容器要么0要么1 |
c.lower_bound(k);(左闭合[) | 返回一个迭代器,指向第一个关键字不小于k的元素,如果没有找到,则返回一个指向给定关键字的插入点,能保持容器中元素顺序的插入位置(意思是在这个位置插入关键字k,整个容器的顺序依然是按照关键字升序的)。 |
c.upper_bound(k);(左开合() | 返回一个迭代器,指向第一个关键字大于k的元素,如果没有找到,则返回一个指向给定关键字的插入点,能保持容器中元素顺序的插入位置。 |
c.equal_range(k); | 返回一个pair,pair表示一个关键字等于k的范围,first——beg,second——end,若k不存在,则pair的两个成员都为end(),可以用以遍历multimap和multiset,这种存有多种相同key值的关联容器。 |
c.at(k); | 返回关键字为k的元素相关联的值,如果没有这个关键字,则抛出异常out_of_range,注意:set是没有此操作的。 |
在multimap或multiset中,相同关键字会相邻存储。
lower_boud、upper_bound的使用:
multimap<int,string> mulmap;
for(auto beg=mulmap.lower_bound(k),end=mulmap.upper_bound(k);
beg!=end;
++beg)
{
......
}
//如果,k在mulmap中已存在
//那么,beg=mulmap.lower_bound(k),得到的是第一个k存储的位置的迭代器
// end=mulmap.upper_bound(k),得到的是第一个大于k存储的位置的迭代器
//则,beg和end的范围就是一个[beg,end)的范围,这范围里都是关键字为k的元素
equal_range的使用:
multimap<int,string> mulmap;
pair<multimap<int,string>::iterator,multimap<int,string>::iterator> pairit=mulmap.equal_range(k);
auto pairit=mulmap.equal_range(k);
auto beg=pairit.first;
auto end=pairit.second;
for(;beg!=end;++beg)
{...}
2. 无序关联容器
2.1 有序关联容器要点
1.容器里的元素对都是无序、不可重复的。
2.2 分类:
容器 | 头文件 | 含义 | 底层实现 |
unordered_map <key_type,mapped_type> | #include<unordered_map> | 无序关联数组,保存关键字-值对,不会对Key进行排序,关键字唯一 | 哈希表 |
unordered_set <value_type> | #include<unordered_set> | 无序关联集合,不会对关键字进行排序。 | 哈希表 |
2.3 关键字类型要求:
1.无序关联容器都是基于哈希表实现的无序容器,因此它们的键必须支持哈希操作。
2.默认情况下,无序关联容器使用 std::hash
来生成键的哈希值。如果键类型没有定义 std::hash
,你必须提供自定义的哈希函数。
3.需要提供一个相等性比较函数,默认使用 ==
运算符。
3. 有序容器和无序容器的比较
容器 | 底层实现 | key是否排序 | key是否可重复 | key是否可更改 | 查询效率 | 增删效率 |
std::set | 红黑树 | 是 | 不是 | 不可 | O(log n) | O(log n) |
sed::map | 红黑树 | 是 | 不是 | 不可 | O(log n) | O(log n) |
std::multimap | 红黑树 | 是 | 是 | 不可 | O(log n) | O(log n) |
std::multiset | 红黑树 | 是 | 是 | 不可 | O(log n) | O(log n) |
std::unordermap | 哈希表 | 不是 | 不是 | 不可 | O(1) | O(1) |
std::unorderset | 哈希表 | 不是 | 不是 | 不可 | O(1) | O(1) |