🧩在之前我们的学习中接触了STL的部分容器,如:vector、list、deque、forword_list 等容器,这些容器统称为序列式容器,因为其底层是线性序列的数据结构,里面存储元素本身。关联式容器也是用来存储数据的,但与序列式容器不同的是,其存储的是
树形结构的关联式容器:
🧩根据应用场景的不同,STL实现了两种不同结构的管理式容器:树形结构和哈希结构。树形结构的关联式容器主要有以下四种:map、set、multimap、multiset。这几个容器使用平衡搜索树(红黑树)作为其底层结构,容器中的元素是一个有序的序列。
键值对:
🧩键值对用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量 key 和 value ,key 代表键值,value 表示与 key 对应的信息。
SGI - STL中关于键值对的定义:
template<class T1,class T2>
struct pair
{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair()
:first(T1())
,second(T2())
{}
pair(const T1& a,const T2& b)
:first(a)
,second(b)
{}
};

set的使用

void test_set1()
{
vector<int> v{ 1,3,5,7,9,2,4,6,8,10,1,3,5,7,9,2,4,6 };
set<int> s(v.begin(), v.end()); // 用vector的迭代器区间构造set
// 1.排序 + 去重
// 使用迭代器遍历set并打印
set<int>::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " "; // 1 2 3 4 5 6 7 8 9 10
++it;
}
cout << endl;
// 使用范围for进行set的遍历
for (auto& e : s)
{
cout << e << " ";
}
cout << endl;
// 2.set的删除
// 先查找,找到了就删除,若没有找到则会报错
auto pos = s.find(6);
if (pos != s.end())
s.erase(pos);
pos = s.find(11);
if (pos != s.end()) // 若没有这一个条件直接删除会报错
s.erase(pos);
//数据在set中就删除,不在不删除也不报错
s.erase(8);
s.erase(12);
}
typedef pair value_type 。map的使用

operator[]

operator[]的原理是:
用
注意:
在元素访问时,有一个与operator[]类似的操作at()(该接口函数并不常用)函数,都是通过 key 找到与 key 对应的 value 然后返回其引用,不同的是:当 key 不存在时,operator[]用默认的 value 与 key 构造键值对插入,返回该默认 value ,at()函数则直接抛异常。
// operator[]的简单实现
mapped_type& operator[](const key_type& k)
{
pair<iterator, bool> ret = insert(make_pair(k, mapped_type()));
return ret.first->second;
}
map的基本使用:
void test_map()
{
map<string, string> m;
// 1.将键值对<"associative","关联的">插入map中,用pair直接来构造键值对
m.insert(pair<string, string>("associative", "关联的"));
// 2.用make_pair来构造键值对
// 好处:不需要声明pair的参数类型,让函数模板自己推演,用起来更方便
m.insert(make_pair("store elements", "存储元素"));
// 3.用operator[]向map中插入元素
m["uniquely"] = "独特的、唯一的";
m["internally"] = "在内部、内部的";
// 使用迭代器去遍历map中的元素,可以得到一个按照key排序的序列
map<string, string>::iterator it = m.begin();
while (it != m.end())
{
//cout << (*it).first << " -> " << (*it).second << endl;
cout << it->first << " -> " << it->second << endl;
++it;
}
cout << endl;
// map中的键值对中key是唯一的,若key已存在map中再次插入则会失败
auto ret = m.insert(make_pair("associative", "关联的"));
if (ret.second)
cout << "不在map中,插入成功" << endl;
else
cout << "键值associative已经存在,插入失败" << ret.first->first << " -> " << ret.first->second << endl;
// 修改map中的value数据
auto ret2 = m.find("associative");
if (ret2 != m.end())
{
//ret2->second.insert(ret2->second.size(), "、联想的");
// 可读性代码优化
string& str = ret2->second;
str.insert(str.size() - 1, "、联想的");
}
// 使用范围for遍历map
for (auto& e : m)
cout << e.first << " -> " << e.second << endl;
cout << endl;
}
使用map统计次数:
void test_map_count()
{
// 统计水果出现的次数
string arr[] = { "香蕉","西瓜","苹果","西瓜","西瓜","苹果" ,"苹果","西瓜","苹果","草莓","苹果","西瓜","榴莲" };
统计次数方式1:
//map countMap;
//for (const auto& str : arr)
//{
// // 字母第一次出现则插入,后续再次出现则++ret->second
// map::iterator it = countMap.find(str);
// if (it != countMap.end())
// it->second++;
// else
// countMap.insert(make_pair(str, 1));
//}
统计次数方式2:
//map countMap;
//for (const auto& str : arr)
//{
// // 先插入map中,若str已经在map中了,insert会返回str所在节点的迭代器,我们++次数即可
// //pair
// auto ret = countMap.insert(make_pair(str, 1));
// if (ret.second == false)
// {
// ret.first->second++;
// }
//}
// 统计次数方式3:
map<string, int> countMap;
for (const auto& str : arr)
{
// 若k不在map中,先插入,返回节点中v对象的引用
// 若k已经在map中,返回k中所在节点中对应v对象的引用
countMap[str]++;
}
for (const auto& str : countMap)
{
cout << str.first << " -> " << str.second << endl;
}
}
利用map进行排序:
struct MapItCompare
{
bool operator()(map<string, int>::iterator x, map<string, int>::iterator y)
{
return x->second > y->second;
}
};
void test_map2()
{
string arr[] = { "香蕉","西瓜","苹果","西瓜","西瓜","苹果" ,"苹果","西瓜","苹果","草莓","苹果","西瓜","榴莲" };
map<string, int> countMap;
for (const auto& str : arr)
countMap[str]++;
// 将数据导入vector中,用vector排序,比较方式用map中的第二个参数比较
vector<map<string, int>::iterator> v;
map<string, int>::iterator countMapIt = countMap.begin();
while (countMapIt != countMap.end())
{
v.push_back(countMapIt);
++countMapIt;
}
// 传入比较方式的仿函数
sort(v.begin(), v.end(), MapItCompare());
// 利用map进行排序 - 拷贝pair数据
map<int, string, greater<int>> sortMap;
for (auto e : countMap)
{
sortMap.insert(make_pair(e.second, e.first));
}
// 利用set进行排序 - 不拷贝
set<map<string, int>::iterator, MapItCompare> sortSet;
countMapIt = countMap.begin();
while (countMapIt != countMap.end())
{
sortSet.insert(countMapIt);
++countMapIt;
}
// 利用优先级队列排序
priority_queue<map<string, int>::iterator, vector<map<string, int>::iterator>, MapItCompare> pq;
countMapIt = countMap.begin();
while (countMapIt != countMap.end())
{
pq.push(countMapIt);
++countMapIt;
}
}
总结:
typedef pair value_type 。multimap的使用
❗ multimap 和 map 的唯一不同就是:map 中的 key 是唯一的,而 multimap 中的 key 是可以重复的。注意:multimap 中没有重载 operator[] 操作。
multiset的使用
❗ multiset 和 set 的唯一不同就是:set 中的 key 是唯一的,而 multiset 中的 key 是可以重复的。