一.map
1.1map概念
简介:
- map中所有元素都是pair
- pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
- 所有元素都会根据元素的键值自动排序
本质:
- . map/multimap属于关联式容器,底层结构是用二叉树实现。
优点:
- 可以根据key值快速找到value值
map和multimap区别:
-
map不允许容器中有重复key值元素
-
multimap允许容器中有重复key值元素
1.2pair模板类
作用
std::pair
是 C++ 标准库中定义的一个模板类,用于将两个值组合为一个类型。它提供了简单的方式来存储和操作两个不同类型的值。
以下是 std::pair
的一些特点和用法:
-
创建
pair
对象:- 使用构造函数,可以指定两个值。
-
访问
pair
对象的值:- 通过
first
和second
成员变量,分别访问第一个和第二个值。
- 通过
-
比较
pair
对象:- 支持
==
、!=
、<
、<=
、>
、>=
运算符的比较操作。
- 支持
-
作为函数的返回值:
pair
可以用作函数的返回值,这样可以方便地返回多个值。
-
作为容器的元素:
pair
可以作为容器(如vector
、map
等)的元素,以存储多个不同类型的值。
例如,以下是使用 std::pair
的一个示例:
#include <iostream>
#include <utility>
int main() {
std::pair<int, std::string> myPair(1, "Hello");
std::cout << myPair.first << " " << myPair.second << std::endl;
return 0;
}
上述代码创建了一个 pair
对象 myPair
,其中第一个值为 1
,第二个值为 "Hello"
。在输出时,可以使用 first
和 second
成员变量来访问这两个值,并输出结果为 1 Hello
。
std::pair
在许多情况下都非常有用,特别是当需要将两个不同类型的值作为一个实体使用时。
创建方式
std::pair
可以通过多种方式进行创建,以下是几种常见的创建方式:
-
使用构造函数:
- 使用带有参数的构造函数来创建
pair
对象,需要指定两个值的类型。
std::pair<int, std::string> myPair(1, "Hello");
- 使用带有参数的构造函数来创建
-
使用
std::make_pair()
函数:- 使用
std::make_pair()
函数可以更方便地创建pair
对象,不需要显式指定类型。
auto myPair = std::make_pair(1, "Hello");
- 使用
-
使用初始化列表:
- 可以使用初始化列表来创建
pair
对象,并指定两个值。
std::pair<int, std::string> myPair = {1, "Hello"};
- 可以使用初始化列表来创建
-
将
pair
作为函数的返回值:pair
可以作为函数的返回值,将多个值一起返回。
std::pair<int, std::string> getValues() { return std::make_pair(1, "Hello"); }
上述示例中,创建了一个 pair
对象,并指定了两个值。可以根据需要选择适合的方式来创建 pair
对象。无论使用哪种方式,pair
都可以方便地将两个不同类型的值组合为一个对象。
创建方式 | 示例代码 |
---|---|
构造函数 | std::pair<int, std::string> myPair(1, "Hello"); |
std::make_pair() | auto myPair = std::make_pair(1, "Hello"); |
初始化列表 | std::pair<int, std::string> myPair = {1, "Hello"}; |
作为函数返回值 | std::pair<int, std::string> getValues() { return std::make_pair(1, "Hello"); } |
1.3map的构造和赋值
map数据类型只能是各种pair
-
默认构造函数:
- 使用默认构造函数创建一个空的
map
容器。
std::map<KeyType, ValueType> myMap;
- 使用默认构造函数创建一个空的
-
初始化列表构造函数:
- 使用初始化列表构造函数,可以在创建
map
的同时初始化一些键-值对。
std::map<KeyType, ValueType> myMap = { {key1, value1}, {key2, value2}, ... };
- 使用初始化列表构造函数,可以在创建
-
范围构造函数:
- 使用另一个容器的范围构造函数,可以将其他容器的内容复制到一个新的
map
中。
std::map<KeyType, ValueType> myMap(otherContainer.begin(), otherContainer.end());
- 使用另一个容器的范围构造函数,可以将其他容器的内容复制到一个新的
-
拷贝构造函数:
- 使用另一个
map
的拷贝构造函数,可以创建一个已有map
的副本。
std::map<KeyType, ValueType> myMap(otherMap);
- 使用另一个
-
赋值运算符:
- 使用赋值运算符,将一个
map
的内容赋值给另一个map
。
std::map<KeyType, ValueType> myMap; myMap = otherMap;
- 使用赋值运算符,将一个
-
移动构造函数和移动赋值运算符:
- C++11 引入了移动语义,可以使用移动构造函数和移动赋值运算符来高效地将一个
map
的内容移动到另一个map
中,避免不必要的复制。
std::map<KeyType, ValueType> myMap(std::move(otherMap)); // 移动构造函数 myMap = std::move(otherMap); // 移动赋值运算符
- C++11 引入了移动语义,可以使用移动构造函数和移动赋值运算符来高效地将一个
这些是使用 map
的常见构造和赋值方式。根据实际需求,选择适当的方法来构造和赋值 map
对象。
构造/赋值方式 | 示例代码 |
---|---|
默认构造函数 | std::map<KeyType, ValueType> myMap; |
初始化列表构造函数 | std::map<KeyType, ValueType> myMap = { {key1, value1}, {key2, value2}, ... }; |
范围构造函数 | std::map<KeyType, ValueType> myMap(otherContainer.begin(), otherContainer.end()); |
拷贝构造函数 | std::map<KeyType, ValueType> myMap(otherMap); |
赋值运算符 | std::map<KeyType, ValueType> myMap; myMap = otherMap; |
移动构造函数 | std::map<KeyType, ValueType> myMap(std::move(otherMap)); |
移动赋值运算符 | myMap = std::move(otherMap); |
1.4map的大小和交换
在C++中,std::map
提供了几种常用的成员函数来获取大小以及进行交换操作。
-
获取大小:
size()
函数可以返回当前map
中键值对的数量。
std::map<KeyType, ValueType> myMap; int size = myMap.size();
-
判断是否为空:
empty()
函数可以判断map
是否为空,返回一个布尔值表示是否为空。
std::map<KeyType, ValueType> myMap; bool isEmpty = myMap.empty();
-
交换 map:
swap()
函数可以交换两个map
容器的内容。
std::map<KeyType, ValueType> map1; std::map<KeyType, ValueType> map2; // 交换 map1 和 map2 的内容 map1.swap(map2);
使用这些操作可以方便地获取 map
的大小,并且在需要时交换 map
容器的内容。记住,在交换时,map
中的元素会被交换,并且两个 map
的键值对会互相替换。
操作 | 示例代码 |
---|---|
获取大小 | std::map<KeyType, ValueType> myMap; int size = myMap.size(); |
判断是否为空 | std::map<KeyType, ValueType> myMap; bool isEmpty = myMap.empty(); |
交换 map | std::map<KeyType, ValueType> map1; std::map<KeyType, ValueType> map2; map1.swap(map2); |
1.5map的插入和删除
在 C++ 中,std::map
提供了几种用于插入和删除元素的成员函数和操作符。
-
插入元素:
- 使用
insert()
函数可以插入一个键值对或一对迭代器范围的键值对。
std::map<KeyType, ValueType> myMap; // 单个插入 myMap.insert(std::make_pair(key, value)); // 插入多个元素 std::map<KeyType, ValueType> anotherMap; myMap.insert(anotherMap.begin(), anotherMap.end());
- 使用
-
删除元素:
- 使用
erase()
函数可以删除指定键的元素,也可以使用迭代器来删除一个或一段元素。
std::map<KeyType, ValueType> myMap; // 删除指定键的元素 myMap.erase(key); // 删除迭代器指向的元素 std::map<KeyType, ValueType>::iterator it = myMap.find(key); if (it != myMap.end()) { myMap.erase(it); } // 删除元素范围 myMap.erase(myMap.begin(), myMap.end());
- 使用
-
清空 map:
- 使用
clear()
函数可以清空整个map
容器中的所有元素。
std::map<KeyType, ValueType> myMap; myMap.clear();
- 使用
这些是 std::map
插入和删除元素的常见操作。根据实际需求,选择适当的方法来插入和删除 map
中的元素。
操作 | 示例代码 |
---|---|
插入单个元素 | std::map<KeyType, ValueType> myMap; myMap.insert(std::make_pair(key, value)); |
插入多个元素 | std::map<KeyType, ValueType> myMap; std::map<KeyType, ValueType> anotherMap; myMap.insert(anotherMap.begin(), anotherMap.end()); |
删除指定键的元素 | std::map<KeyType, ValueType> myMap; myMap.erase(key); |
删除迭代器指向的元素 | std::map<KeyType, ValueType> myMap; std::map<KeyType, ValueType>::iterator it = myMap.find(key); if (it != myMap.end()) { myMap.erase(it); } |
删除元素范围 | std::map<KeyType, ValueType> myMap; myMap.erase(myMap.begin(), myMap.end()); |
清空 map | std::map<KeyType, ValueType> myMap; myMap.clear(); |
1.6map的查找和统计
在 C++ 中,std::set
提供了几种用于查找和统计元素的成员函数。
-
查找元素:
- 使用
find()
函数来查找指定值的元素,如果找到则返回指向该元素的迭代器,否则返回end()
迭代器。
std::set<ValueType> mySet; // 查找指定值的元素 std::set<ValueType>::iterator it = mySet.find(value); if (it != mySet.end()) { // 元素找到 }
- 使用
-
统计元素个数:
- 使用
count()
函数来统计指定值的元素在set
中出现的次数。
std::set<ValueType> mySet; // 统计指定值的元素个数 int count = mySet.count(value);
- 使用
-
判断元素是否存在:
- 使用
count()
函数来判断指定值的元素是否存在于set
中(存在时返回 1 个或更多的计数)。
std::set<ValueType> mySet; // 判断指定值的元素是否存在 bool exists = mySet.count(value) > 0;
- 使用
这些是 std::set
查找和统计元素的常见操作。你可以根据实际需求,使用这些函数来查找特定的元素并统计元素的个数。
操作 | 示例代码 |
---|---|
查找指定值的元素 | std::set<ValueType> mySet; std::set<ValueType>::iterator it = mySet.find(value); if (it != mySet.end()) { /* 元素找到 */ } |
统计指定值的个数 | std::set<ValueType> mySet; int count = mySet.count(value); |
判断元素是否存在 | std::set<ValueType> mySet; bool exists = mySet.count(value) > 0; |
这个表格将 set
的查找和统计操作整理成了一个简洁的表格。你可以根据实际需求使用这些操作来查找指定值的元素、获取指定值在集合中的个数,或者判断元素是否存在于集合中。
1.7map容器排序
map容器排序,和set排序一样,都需要借用仿函数。
在 C++ 中,std::map
是一个关联容器,按照键的自动排序进行存储。默认情况下,std::map
使用键的升序进行排序。但如果你希望按照其他方式进行排序,可以通过自定义比较函数来实现。
以下是一些实现 std::map
容器排序的示例代码:
#include <iostream>
#include <map>
#include <functional>
// 自定义比较函数,按照值的降序进行排序
struct CompareByValue {
bool operator()(const int& a, const int& b) const {
return a > b; // 使用大于号实现降序
}
};
int main() {
std::map<int, std::string> myMap; // 默认按照键的升序排序
// 添加元素
myMap.insert(std::make_pair(3, "C"));
myMap.insert(std::make_pair(1, "A"));
myMap.insert(std::make_pair(2, "B"));
// 遍历输出,默认按照键的升序输出
std::cout << "默认升序排序:" << std::endl;
for (const auto& pair : myMap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
// 使用自定义比较函数,按照值的降序排序
std::map<int, std::string, CompareByValue> sortedMap;
// 复制原始 map 的元素到排序后的 map
sortedMap.insert(myMap.begin(), myMap.end());
// 遍历输出,按照值的降序输出
std::cout << "按值降序排序:" << std::endl;
for (const auto& pair : sortedMap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
在示例代码中,CompareByValue
结构体是一个自定义的比较函数对象,实现了按照值的降序进行排序。在创建 std::map
容器时,可以传入 CompareByValue
类型作为第三个模板参数来指定自定义比较函数。
注:如果你想对 std::map
进行临时排序而不影响原始数据的排序,你可以使用 std::multimap
或在复制数据后进行排序。
二.multimap
multimap是键值可以重复的map。
std::map
和 std::multimap
之间的区别:
-
唯一性:
std::map
要求键的唯一性,每个键只能对应一个值。而std::multimap
允许键的重复,即可以存储多个具有相同键的值。 -
排序:
std::map
按照键的默认严格弱序进行排序,这有助于提高搜索效率。默认情况下,键是按照升序排序的。而std::multimap
也按照键的默认严格弱序进行排序,即也是按照升序排序的。 -
删除:对于
std::map
,删除给定键的元素将删除具有该键的唯一元素。而对于std::multimap
,删除给定键的元素将删除所有具有该键的元素。 -
multimap没有重载[ ],map可以直接用[]访问,修改,插入数据。
总结起来,std::map
适合存储唯一键值对并按照键进行排序的场景。而 std::multimap
适合存储允许键重复的键值对,并且也可以按照键进行排序。
三.unordered_map
在函数接口方面,std::map
和 std::unordered_map
之间有一些区别和差异。以下是其中的一些主要区别:
-
插入元素:对于
std::map
,可以使用insert()
函数或[]
运算符插入一个键值对。如果已经存在相同的键,则插入不会生效。而对于std::unordered_map
,也可以使用insert()
函数或[]
运算符插入一个键值对,但如果已经存在相同的键,则新的值将取代旧的值。 -
查找元素:对于
std::map
,可以使用find()
函数根据键查找元素,并返回一个迭代器指向找到的元素,如果没找到则返回末尾迭代器。而对于std::unordered_map
,也可以使用find()
函数根据键查找元素,返回一个迭代器指向找到的元素,如果没找到则返回和end()
相等的迭代器。 -
访问元素:对于
std::map
,可以使用[]
运算符根据键访问元素的值。而对于std::unordered_map
,同样可以使用[]
运算符根据键访问元素的值。 -
删除元素:对于
std::map
,可以使用erase()
函数删除给定键的元素,如果存在多个具有相同键的元素,只会删除第一个匹配的元素。而对于std::unordered_map
,可以使用erase()
函数删除给定键的元素,它会删除所有具有相同键的元素。
除了上述不同之外,其他函数接口如迭代器操作、大小操作、清除容器等方面在 std::map
和 std::unordered_map
中基本上是相似的。
四.unordered_multimap
std::map
和 std::unordered_map
是两个不同的关联容器,而 std::unordered_multimap
则是 std::unordered_map
的多重键版本。下面是 std::unordered_multimap
和 std::map
的一些主要区别:
-
允许重复键:在
std::map
中,每个键只能对应唯一的值,如果插入具有相同键的元素,则会替换旧值。然而,在std::unordered_multimap
中,可以插入具有相同键的多个元素,并且允许重复键的存在。 -
元素顺序:
std::map
是有序关联容器,它按照键的严格弱顺序进行排序。在遍历std::map
时,元素将按照排序顺序访问。而std::unordered_multimap
是无序关联容器,它不保证元素的顺序,元素的存储和访问顺序可能会有所不同。 -
查找效率:由于
std::unordered_multimap
基于哈希表实现,查找操作的平均时间复杂度接近 O(1),而std::map
则是基于红黑树,查找操作的平均时间复杂度为 O(log n)。 -
插入顺序:在
std::map
中,每个元素按照键的顺序进行插入。而在std::unordered_multimap
中,元素的插入顺序与它们的哈希值相关,哈希值不一样的元素可能会以不同的顺序插入。
根据需要,选择使用 std::map
、std::unordered_map
还是 std::unordered_multimap
取决于数据结构的要求和性能考虑。如果需要有序访问和唯一键,可以选择 std::map
;如果需要快速查找,并且键可以重复,可以选择 std::unordered_multimap
。