一、关联式容器
关联式容器是用来存储数据的,与序列式容器(vector,list,deque等)不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高。
二、键值对
键值对是用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。
三、树形结构的关联式容器
根据应用场景的不同, STL 总共实现了两种不同结构的管理式容器:树型结构与哈希结构。
树型结 构的关联式容器主要有四种: map 、 set 、 multimap 、 multiset 。
这四种容器的共同点是:
使用平衡搜索树( 即红黑树 ) 作为其底层结果,容器中的元素是一个有序的序列。
1、set
1.1 set的介绍
1)set 是按照一定次序存储元素的容器。
2) 在 set 中,元素的 value 也标识key( value 就是 key ,类型为 T) ,并且每个 value 必须是唯一的。
set 中的元素不能在容器中修改 ( 元素总是 const) ,但是可以从容器中插入或删除它们。
3) 在内部, set 中的元素总是按照其内部比较对象 ( 类型比较 ) 所指示的特定严格弱排序准则进行
排序。
4)set 容器通过 key 访问单个元素的速度通常比 unordered_set 容器慢,但它们允许根据顺序对
子集进行直接迭代。
5)set 在底层是用二叉搜索树 ( 红黑树 ) 实现的。
注意:
1) 与 map/multimap 不同, map/multimap 中存储的是真正的键值对 <key, value> , set 中只放
value ,但在底层实际存放的是由 <value, value> 构成的键值对。
2)set 中插入元素时,只需要插入 value 即可,不需要构造键值对。
3) set 中的元素不可以重复 ( 因此可以使用 set 进行去重 ) 。
4) 使用 set 的迭代器遍历 set 中的元素,可以得到有序序列。
5)set 中的元素默认按照小于来比较。
6)set 中查找某个元素,时间复杂度为:logn
7)set 中的元素不允许修改。
8)set 中的底层使用二叉搜索树 ( 红黑树 ) 来实现。
1.2 set的使用
set的模板参数列表

T: set 中存放元素的类型,实际在底层存储 <value, value> 的键值对。
Compare : set 中元素默认按照小于来比较。
Alloc : set 中元素空间的管理方式,使用 STL 提供的空间配置器管理。
set的使用举例
cpp
#include <set>
void TestSet()
{
// 用数组array中的元素构造set
int array[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0, 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
set<int> s(array, array+sizeof(array)/sizeof(array));
cout << s.size() << endl;
// 正向打印set中的元素,从打印结果中可以看出:set可排序+去重
for (auto& e : s)
cout << e << " ";
cout << endl;
// 使用迭代器逆向打印set中的元素
for (auto it = s.rbegin(); it != s.rend(); ++it)
cout << *it << " ";
cout << endl;
// set中值为3的元素出现了几次
cout << s.count(3) << endl;
}
2、map
2.1 map的介绍
1)map 是关联容器,它按照特定的次序 ( 按照 key 来比较 ) 存储由键值 key 和值 value 组合而成的元
素。
2) 在 map 中,键值 key 通常用于排序和惟一地标识元素,而值 value 中存储与此键值 key 关联的
内容。键值 key 和值 value 的类型可能不同,并且在 map 的内部, key 与 value 通过成员类型
value_type 绑定在一起,为其取别名称为 pair: typedef pair<const key, T> value_type;
3) 在内部, map 中的元素总是按照键值 key 进行比较排序的。
4)map 中通过键值访问单个元素的速度通常比 unordered_map 容器慢,但 map 允许根据顺序
对元素进行直接迭代 ( 即对 map 中的元素进行迭代时,可以得到一个有序的序列 ) 。
5)map 支持下标访问符,即在 [] 中放入 key ,就可以找到与 key 对应的 value 。
6)map 通常被实现为二叉搜索树 ( 更准确的说:平衡二叉搜索树 ( 红黑树 )) 。
2.2 map的使用
map的模板参数

key: 键值对中 key 的类型。
T : 键值对中 value 的类型。
Compare: 比较器的类型, map 中的元素是按照 key 来比较的,缺省情况下按照小于来比较,一般情况下( 内置类型元素 ) 该参数不需要传递,如果无法比较时 ( 自定义类型 ) ,需要用户自己显式传递比较规则( 一般情况下按照函数指针或者仿函数来传递 )。
Alloc :通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的空间配置器。
注意:
1)map 中的的元素是键值对。
2)map 中的 key 是唯一的,并且不能修改。
3) 默认按照小于的方式对 key 进行比较。
4)map 中的元素如果用迭代器去遍历,可以得到一个有序的序列。
5)map 的底层为平衡搜索树 ( 红黑树 ) ,查找效率比较高O(logn)。
6) 支持 [] 操作符, operator[] 中实际进行插入查找。
map的使用举例
cpp
void test_map1()
{
map<string, string> dict;
pair<string, string> kv1("insert", "插入");
dict.insert(kv1);
dict.insert(pair<string, string>("sort", "排序"));
// C++98
dict.insert(make_pair("string", "字符串"));
// C++11 多参数的构造函数隐式类型转换
dict.insert({ "string", "字符串" });
// 隐式类型的转换
string str1 = "hello";
A aa1 = { 1, 2 };
pair<string, string> kv2 = { "string", "字符串" };
}
void test_map2()
{
map<string, string> dict;
dict.insert(make_pair("string", "字符串"));
dict.insert(make_pair("sort", "排序"));
dict.insert(make_pair("insert", "插入"));
// 不插入,不覆盖;插入过程中,只比较key,value是相同无所谓
// key已经有了就不插入了
dict.insert(make_pair("insert", "xxxx"));
//map<string, string>::iterator it = dict.begin();
auto it = dict.begin();
while (it != dict.end())
{
//cout << (*it).first << ":" << (*it).second << endl;
cout << it->first << ":" << it->second << endl;
++it;
}
cout << endl;
for (const auto& kv : dict)
{
cout << kv.first << ":"<<kv.second<<endl;
}
}
void test_map3()
{
// 统计次数
string arr[] = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
map<string, int> countMap;
for (auto e : arr)
{
countMap[e]++;
}
for (const auto& kv : countMap)
{
cout << kv.first << ":" << kv.second << endl;
}
}
void test_map4()
{
map<string, string> dict;
dict.insert(make_pair("string", "字符串"));
dict.insert(make_pair("sort", "排序"));
dict.insert(make_pair("insert", "插入"));
cout << dict["sort"] << endl; // 查找和读
dict["map"]; // 插入
dict["map"] = "映射,地图"; // 修改
dict["insert"] = "xxx"; // 修改
dict["set"] = "集合"; // 插入+修改
}
3、multiset
3.1 multiset的介绍
1)multiset 是按照特定顺序存储元素的容器,其中元素是可以重复的。
2) 在 multiset 中,元素的 value 也会识别它 ( 因为 multiset 中本身存储的就是 <value, value> 组成的键 值对,因此value 本身就是 key , key 就是 value ,类型为 T). multiset 元素的值不能在容器中进行 修改( 因为元素总是 const 的 ) ,但可以从容器中插入或删除。
3) 在内部, multiset 中的元素总是按照其内部比较规则 ( 类型比较 ) 所指示的特定严格弱排序准则
进行排序。
4)multiset 容器通过 key 访问单个元素的速度通常比 unordered_multiset 容器慢,但当使用迭
代器遍历时会得到一个有序序列。
5)multiset 底层结构为二叉搜索树 ( 红黑树 ) 。
注意:
1)multiset 中在底层中存储的是 <value, value> 的键值对。
2)mtltiset 的插入接口中只需要插入即可。
3) 与 set 的区别是, multiset 中的元素可以重复, set 是中 value 是唯一的。
4) 使用迭代器对 multiset 中的元素进行遍历,可以得到有序的序列。
5)multiset 中的元素不能修改。
6) 在 multiset 中找某个元素,时间复杂度为O(logn)。
7)multiset 的作用:可以对元素进行排序。
3.2 multiset的使用
此处只简单演示 set 与 multiset 的不同,其他接口与 set 相同。
cpp
#include <set>
void TestSet()
{
int array[] = { 2, 1, 3, 9, 6, 0, 5, 8, 4, 7 };
// 注意:multiset在底层实际存储的是<int, int>的键值对
multiset<int> s(array, array + sizeof(array)/sizeof(array[0]));
for (auto& e : s)
cout << e << " ";
cout << endl;
return 0;
}
4、multimap
4.1 multimap的介绍
1)Multimaps 是关联式容器,它按照特定的顺序,存储由 key 和 value 映射成的键值对 <key,
value> ,其中多个键值对之间的 key 是可以重复的。
2) 在 multimap 中,通常按照 key 排序和惟一地标识元素,而映射的 value 存储与 key 关联的内
容。 key 和 value 的类型可能不同,通过 multimap 内部的成员类型 value_type 组合在一起,
value_type 是组合 key 和 value 的键值对 : typedef pair<const Key, T> value_type;
3) 在内部, multimap 中的元素总是通过其内部比较对象,按照指定的特定严格弱排序标准对
key 进行排序的。
4)multimap 通过 key 访问单个元素的速度通常比 unordered_multimap 容器慢,但是使用迭代
器直接遍历 multimap 中的元素可以得到关于 key 有序的序列。
5)multimap 在底层用二叉搜索树 ( 红黑树 ) 来实现。
注意:
multimap 和 map 的唯一不同就是: map 中的 key 是唯一的,而 multimap 中 key 是可以 重复的 。
4.2 multimap的使用
multimap 中的接口可以参考 map ,功能都是类似的。
注意:
1)multimap 中的 key 是可以重复的。
2)multimap 中的元素默认将 key 按照小于来比较。
3)multimap 中没有重载 operator[] 操作 。
4) 使用时与 map 包含的头文件相同。
四、在OJ中的使用
692. 前K个高频单词 - 力扣(LeetCode)
cpp
class Solution
{
public:
struct Greater
{
bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2)
{
return kv1.second > kv2.second || (kv1.second == kv2.second && kv1.first < kv2.first);
}
};
vector<string> topKFrequent(vector<string>& words, int k)
{
map<string, int> countMap;
for(const auto& e : words)
{
countMap[e]++;
}
vector<pair<string, int>> kv(countMap.begin(), countMap.end());
sort(kv.begin(), kv.end(), Greater());
vector<string> v;
for(int i = 0; i < k; i++)
{
v.push_back(kv[i].first);
}
return v;
}
};
cpp
class Solution
{
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2)
{
set<int> s1(nums1.begin(), nums1.end());
set<int> s2(nums2.begin(), nums2.end());
vector<int> v;
set<int>::iterator it1 = s1.begin();
set<int>::iterator it2 = s2.begin();
while(it1 != s1.end() && it2 != s2.end())
{
if(*it1 < *it2)
{
it1++;
}
else if(*it1 > *it2)
{
it2++;
}
else
{
v.push_back(*it1);
it1++;
it2++;
}
}
return v;
}
};