1. 关联容器概述
1.1 关联容器类型
关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的。两个主要的关联容器类型是map和set。map 中的元素是一些关键字—值对,关键字起到索引的作用,值是与关键字相关联的数据。set 中每个元素为一个关键字,set 支持高效的关键字查询操作。
标准库提供8个关联容器:
容器类型 | 含义 | 容器类型 | 含义 |
---|---|---|---|
map | 关联数组,保存关键字—值对 | unordered_map | 用哈希函数组织的 map |
set | 关键字即值,即只保存关键字的容器 | unordered_set | 用哈希函数组织的 set |
multimap | 关键字可重复出现的 map | unordered_multimap | 哈希组织的 map,关键字可重复出现 |
multiset | 关键字可重复出现的 set | unordered_multiset | 哈希组织的 set,关键字可重复出现 |
类型 map 和 multimap 定义在头文件 map 中,set 和 multiset 定义在头文件 set 中,无序容器则分别定义在相关的头文件 unordered_map 和 unordered_set 中。
1.2 使用关联容器
虽然大多数人熟悉诸如 vector 和 list 这样的数据结构,但从未使用过关联结构。在学习标准库关联容器类型之前,我们首先来看一个如何使用这类容器的例子。
1.使用map
map <string, size_t> word_count;
string word;
while(cin >> word)++ word_count[word];
for(const auto & w : word_count)cout << w.first << "occurs" << w.second <<((w.second>1) ? "times" : "time") << endl;
2.使用set
map <string,size_t> word_count;
set<string>exclude = {"the","but","and","or"};
string word;
while(cin >> word){// 只统计不在exclude中的单词if(exclude.find(word) == exclude.end())++ word_count[word];
}
2. 关联容器定义
关联容器不支持顺序容器的位置相关操作,例如push_front或push_back。原因是关联容器中的元素是根据关键字存储的,这些操作对关联容器没有意义。而且,关联容器也不支持构造函数或插入操作这些接受一个元素值和一个数量值的操作。
2.1 定义关联容器
当定义一个map时,必须即指明关键字类型又指明值类型,而定义一个set时,只需要指明关键字类型,因为 set 中没有值。每个关联容器都定义一个默认构造函数,它创建一个指定类型的空容器,我们可以将关联容器初始化为另一个同类型容器的拷贝,或是从一个值范围来初始化关联容器。只要这些值可以转化为容器所需类型就可以。
set<string> exclude = {"the","but","and","or"};
map<string, string>authors = {{"john","jame"},{"aus","jane"},{"dick","chars"}};
1.初始化multimap或multiset
一个 map 或 set 中的关键字必须是唯一的,即一个容器内关键字不会重复。容器multimap和multiset没有此限制,它们都允许多个值对应相同的关键字。如下所示:
vector<int> ivec;
for(vector<int>::size_type i = 0; i != 10; ++i){ivec.push_back(i);ivec.push_back(i);
}
set<int> iset(ivec.cbegin(), ivec.cend());
multiset<int> miset(ivec.cbegin(), ivec.cend());
cout << ivec.size() << endl; // 打印出20
cout << iset.size() << endl; // 打印出10
cout << miset.size() << endl; // 打印出20
2.2 关键字类型的要求
关联容器对于包含的关键字类型有一些限制,对于有序容器 map、set 和 multimap、multiset,关键字类型必须定义元素比较的方法。默认情况下,标准库使用关键字类型的小于运算符来比较两个关键字。在 set 集合中,关键字类型就是元素类型;在 map 中,关键字类型是元素的第一部分的类型。
1.有序容器的关键字类型
可以向一个算法提供我们自定义的比较操作,类似的也可以提供自己定义的操作来替代关键字上的小于运算符。实际定义的操作可能是一个复杂的函数,但无论我们怎么定义比较函数,它必须具备如下性质:
1.两个关键字不能同时小于等于对方;如果 K1 小于等于 K2,那么 k2 绝不能小于等于 k1;
2.如果 k1 小于等于 k2,且 k2 小于等于 k3,那么 k1 必须小于等于 k3;
3.如果存在两个关键字,任何一个都不小于等于另一个,那么我们称这两个关键字是等价的。
2.使用关键字类型的比较函数
一个容器中元素的操作类型也是该容器类型的一部分。为了指定使用自定义的操作,必须在定义关联容器类型时提供此操作的类型。自定义的操作类型在尖括号中紧跟着元素类型给出。
#include <iostream>
#include <set>
// 比较函数,按照元素的绝对值大小进行比较
bool compareAbs(int a, int b) {return std::abs(a) < std::abs(b);
int main() {// 使用函数指针作为比较函数std::set<int, bool(*)(int, int)> mySet(compareAbs);mySet.insert(-5);mySet.insert(3);mySet.insert(-2);mySet.insert(7);// 输出容器中的元素,按照绝对值从小到大排序for (int num : mySet) {std::cout << num << " ";}return 0;
}
2.3 pair类型
pair 是 C++ 标准库中定义的一个模板类,用于存储一对值,即键值对,通常用于关联容器的元素类型。pair 提供了两个公共成员变量 first 和 second,用来存储两个值。使用make_pair 需要添加头文件utility。
#include <iostream>
#include <utility>
int main() {// 创建一个 std::pair 对象std::pair<int, double> myPair