顺序容器的底层构造是数组和链表,而关联容器的底层是红黑树、哈希表,提供高效的查找、添加和删除操作。关联容器与顺序容器相比的优势:
- 具有高效的关键字查找和访问
- 支持组合(关键字+值)的存储方式
一、关联容器概述
关联容器的类型总结如下:
- map,保存“关键字-值”
- set,关键字即值,只保存一个“值”
- multimap,关键字可重复的map
- multiset,关键字可重复的set
- unordered_map,无序map
- unordered_set,无序set
- unordered_multimap,无序可重复map
- unordered_multiset,无序可重复set
总结一下,主要是map和set两种,衍生出可重复和无序的各种版本。
各自具有各自的头文件,例如map的头文件< map >
二、map和set
map存储的是“关键字”和“值”的组合体。
map是按关键字排序的有序序列,并且关键字唯一。
set存储的“关键字”。
set也是按关键字排序的有序序列,并且关键字唯一。
map主要用于存储两个关联单元,比如存储图书名和对应的作者。
set主要用于频繁查询,比如在挨个处理单词时,想把“a”,“the”等词删除,那么就把这些词放在set中,处理时把每个单词到set中查一下,如果有,就删掉。
其余的版本都是它俩的扩充,可以根据实际需要选择。
三、map和set的使用
map和set唯一的区别就是map里放的是绑定的两个值(关键字-值),而set放的单个值,在存储方面有点类似于顺序容器,但是操作比顺序容器高效。
- 定义
//map
//未初始化
map<string,int> mp1;
//列表初始化
map<string,int> mp2 = {{"a",1},{"b",2}};
//set
//未初始化
set<string> st1;
//列表初始化
set<string> st2 = {"a","b","c"};
map与set的定义只是类型上的不同,map需要指明两个类型,set只需一个。
- 访问
//map
//遍历访问
for(auto i = mp2.cbegin(); i != mp2.cend(); i++)
{
cout << i->first << i->second << endl;
//等同于
cout << (*i).first << (*i).second << endl;
}
//按关键字访问
cout << mp2["a"];//输出的是1
//set
for(auto i = st2.cbegin(); i != st2.cend(); i++)
{
cout << *i << endl;
}
其中遍历访问与顺序容器类似,因为map存储的是“关键字-值”的类型,所以需要用first访问关键字,用second访问值。
有意思的是按关键字访问,map按关键字升序存放,所以支持以关键字为下标的访问,返回的是关键字对应的值。set不支持,因为它就一个值。
set的访问和顺序容器一样。
- 添加元素
//map
//单个插入
mp2.insert(pair<string,int>("c",2));
//列表插入
mp2.insert({{"d",1},{"e",1}});
//迭代器插入
mp2.inset(b,e);
//set
//单个插入
st2.insert("abc");
//迭代器插入
st2.insert(b,e);
map比较常用的单个插入和列表插入。其中pair是将string和int类型的两个值绑定在一起。
set的迭代器插入很方便,可以将指向顺序容器中的迭代器范围内的元素插入,前提是类型得相同。
- 查询
//查找
auto f = mp2.find("a");
//计数
auto c = count("a");
在map中查询和计数效果一样,因为map关键字唯一。在multimap中,关键字相同的按顺序放在一起,所以可以先计数,然后找到第一个,按数量顺序访问。
set类似,它只有一个值。
- 删除
//按关键字删除
auto e1 = mp2.erase("a");
//按迭代器删除
auto e2 = mp2.erase(p);
set和map都一样,都是根据关键字或者迭代器删除,map删除的是“关键字-值”,set删除的是关键字。
四、其他关联容器
map/set关键字唯一,当关键字不唯一的时候,可以用mutimap或者mutiset。
map/set自动按升序排序,当顺序不重要的时候,可以用unordered_map或者unordered_set。
无序容器不按顺序排序,输出的顺序和有序容器可能不同。顺序可能不同,但结果是相同的。