1 std::multimap 概述
std::multimap 是 C++ 标准库中的一个容器,它存储的元素都是键值对,并且允许有重复的键。与 std::map 不同,std::multimap 允许存储多个具有相同键的元素。在 std::multimap 中,键用于对元素进行排序,并且排序规则可以由用户定义。
multimap 的内部实现通常基于红黑树这种平衡二叉搜索树,因此元素的搜索、插入和删除操作都具有对数级的时间复杂度。由于这种数据结构特性,multimap 的搜索效率非常高,可以快速找到某一键值下的所有元素位置。
multimap提供了一系列成员函数来操作容器中的元素,包括插入数据(insert)、删除数据(erase)、查找数据(find)、清空数据(clear)、判空(empty)、获取有效元素的大小(size)、获取键值中查找元素的个数(count)等。此外,它还提供了反向迭代器(rbegin 和 rend),可以用于反向遍历容器中的元素。
需要注意的是,multimap 中元素的 key 在容器中是不能被修改的。因此,multimap 更适用于需要存储重复元素且需要保持元素有序的场景。然而,如果插入和删除操作非常频繁,multimap 可能不是最佳选择,因为每次插入或删除都可能导致树的重新平衡,从而影响性能。
1.1 std::multimap 的内部实现
std::multimap 的内部实现主要基于红黑树这种数据结构。红黑树是一种自平衡的二叉搜索树,通过着色和特定的调整规则来保持树的平衡,从而在动态数据插入、删除和查找过程中保持相对高效的性能。
在std::multimap 中,红黑树的特性使得元素总是保持有序。每个节点存储一个元素,并且满足二叉搜索树的性质:对于每个节点,其左子树中的所有元素都小于该节点,其右子树中的所有元素都大于该节点。此外,红黑树还通过颜色和旋转操作来维护树的平衡,确保树的深度不会过大,从而保证了操作的效率。
由于红黑树的这些特性,std::multimap 的插入、删除和查找操作都能保持对数级的时间复杂度,即 O(log n),其中n是容器中元素的数量。这使得 std::multimap 在处理大量数据时仍能保持高效的性能。
以下是一些关于 std::multimap 内部实现中红黑树数据结构的关键点:
(1)节点结构: 红黑树的每个节点包含多个信息,包括元素的值、节点的颜色(红色或黑色)、指向左子节点和右子节点的指针。节点颜色的信息对于维护树的平衡性至关重要。
(2)性质: 红黑树满足以下五个关键性质,这些性质保证了树的平衡性和搜索效率:
- 每个节点要么是红色,要么是黑色。
- 根节点是黑色。
- 所有叶子节点(NIL 或空节点,通常不显式表示)是黑色。
- 如果一个节点是红色的,则它的两个子节点都是黑色(从每个叶子到根的所有路径上不能有两个连续的红色节点)。
- 对于每个节点,从该节点到其所有后代叶子节点的简单路径上,均包含相同数目的黑色节点。
(3)插入操作: 当向 multimap 中插入一个新元素时,首先会像普通的二叉搜索树一样找到新元素应该插入的位置,然后执行插入操作。插入后,可能需要通过颜色调整和旋转操作来重新平衡树,以满足红黑树的性质。
(4)删除操作: 删除操作相对复杂一些,因为需要确保在删除节点后树仍然保持平衡。删除操作可能涉及节点的合并、颜色的更改以及树的旋转。
(5)查找操作: 由于红黑树是一种二叉搜索树,因此查找操作与普通的二叉搜索树类似。从根节点开始,根据节点的值和搜索的键值进行比较,决定是向左子树还是向右子树递归查找,直到找到目标元素或确定元素不存在。
通过红黑树的这些内部实现机制,std::multimap能够高效地维护元素的排序状态,并支持快速的插入、删除和查找操作。
1.2 std::multimap 的性能特点
std::multimap 的性能特点主要体现在以下几个方面:
(1)有序性: std::multimap 是一个有序集合容器,它根据元素的键值自动进行排序。这种有序性使得元素可以按照特定的顺序进行存储和检索,从而方便了对元素的排序和查找操作。
(2)插入与删除的对数级复杂度: 由于 std::multimap 内部采用红黑树作为数据结构,因此其插入和删除操作的时间复杂度都是对数级的,即 O(log n),其中 n 是容器中元素的数量。这意味着,无论容器中有多少元素,插入和删除操作的效率都能保持相对稳定,不会随着元素数量的增加而显著下降。
(3)高效的查找性能: 同样得益于红黑树的特性,std::multimap 的查找操作也具有对数级的时间复杂度。这使得在大量元素中快速定位到特定元素成为可能,提高了程序的执行效率。
(4)允许重复元素: 与 std::map 不同,std::multimap 允许存储重复的元素。这意味着在同一个 multimap 容器中,可以有多个具有相同键值的元素存在。这一特性使得 multimap 在某些需要处理重复元素的场景中非常有用。
(5)空间消耗: 由于 std::multimap 使用红黑树作为内部数据结构,而红黑树在维护平衡的过程中可能需要额外的空间来存储节点的颜色和进行旋转操作。因此,相对于其他简单的数据结构(如数组或链表),multimap 的空间消耗可能会稍高一些。但需要注意的是,这种空间消耗是为了换取更高的操作效率而付出的代价。
需要注意的是,虽然 std::multimap 具有上述性能特点,但在某些特定场景下可能并不是最优选择。例如,如果需要进行频繁的插入和删除操作,并且不关心元素的顺序,那么使用其他数据结构(如哈希表)可能会更加高效。
2 std::multimap 的基本使用
2.1 std::multimap 的声明与初始化
声明
首先,需要包含<map>头文件以使用 std::multimap:
#include <map>
#include <string>
// 声明一个键与值都是整数类型的 multimap
std::multimap<int,int> vals;
// 声明一个键是字符串类型,值是整数类型的 multimap
std::multimap<std::string,int> strs;
// 声明一个键是自定义类型,值是整数类型的 multimap
struct MyStruct
{
bool operator<(const MyStruct& data) const
{
return id < data.id;
}
int id;
std::string name;
};
std::multimap<MyStruct,int> myStructs;
初始化
可以使用多种方法来初始化 std::multimap。
(1)默认初始化:
创建一个空的 std::multimap 容器。
std::multimap<int,int> vals; // 空的 multimap
(2)使用初始化列表:
在声明时直接使用初始化列表来添加元素,元素会按照排序顺序插入。
std::multimap<int, int> vals = { {1,2}, {2,3}, {2,4}, {4,5}, {5,6} };
// vals 现在包含元素:{1,2}, {2,3}, {2,4}, {4,5}, {5,6}
(3)复制初始化:
使用另一个 std::multimap 来初始化新的 std::multimap。
std::multimap<int, int> firstMap = { {1,2}, {2,3}, {2,4}, {4,5}, {5,6} };
std::multimap<int, int> secondMap(firstMap); // 复制初始化
// firstMap 与 secondMap 现在都包含元素:{1,2}, {2,3}, {2,4}, {4,5}, {5,6}
(4)移动初始化:
使用另一个 std::multimap 的移动构造函数来初始化新的 std::multimap。
std::multimap<int, int> firstMap = { {1,2}, {2,3}, {2,4}, {4,5}, {5,6} };
std::multimap<int, int> secondMap(std::move(firstMap)); // 移动初始化
// secondMap 现在包含元素:{1,2}, {2,3}, {2,4}, {4,5}, {5,6}
// firstMap 现在是一个空的 multimap
2.2 std::multimap 的大小与容量
std::multimap:中与大小与容量相关的方法有如下几种:
- empty() const;: 检查 multimap 是否为空。
- size() const;: 返回 multimap 中的元素数量。
- max_size() const;: 返回 multimap 可能包含的最大元素数量。
如下为样例代码:
#include <iostream>
#include <map>
#include <string>
int main()
{
// 创建一个空的 std::multimap
std::multimap<std::string, int> myMap;
// 检查 multimap 是否为空
if (myMap.empty()) {
std::cout << "multimap is empty.\n";
}
else {
std::cout << "multimap is not empty.\n";
}
// 插入一些元素
myMap.insert(std::make_pair("Apple", 1));
myMap.insert(std::make_pair("Apple", 2));
myMap.insert(std::make_pair("Banana", 3));
myMap.insert(std::make_pair("Banana", 4));
// 再次检查 multimap 是否为空
if (myMap.empty()) {
std::cout << "multimap is still empty.\n";
}
else {
std::cout << "multimap is now not empty.\n";
}
// 获取 multimap 的大小
std::cout << "Size of the multimap: " << myMap.size() << "\n";
// 获取 multimap 的最大可能大小
std::cout << "Maximum possible size of the multimap: " << myMap.max_size() << "\n";
// 遍历 multimap 并输出元素
for (const auto& pair : myMap) {
std::cout << pair.first << ": " << pair.second << "\n";
}
// 清除 multimap 中的所有元素
myMap.clear();
// 再次检查 multimap 是否为空
if (myMap.empty()) {
std::cout << "multimap is empty again.\n";
}
else {
std::cout << "multimap is not empty after clearing.\n";
}
return 0;
}
上面代码的输出为:
multimap is empty.
multimap is now not empty.
Size of the multimap: 4
Maximum possible size of the multimap: 230584300921369395
Apple: 1
Apple: 2
Banana: 3
Banana: 4
multimap is empty again.
这个示例首先创建了一个空的 std::multimap,并使用 empty() 方法检查它是否为空。然后,插入了一些元素,并再次检查它是否为空。接着,使用 size() 方法来获取 multimap 中的元素数量,并将其输出。之后,使用 max_size() 方法来获取 multimap 可能包含的最大元素数量(这通常是一个非常大的值,代表了理论上可能存储的元素的最大数量,实际上很少会达到这个限制)。最后,遍历了 multimap 并输出了每个键值对,然后清除了 multimap 中的所有元素,并再次检查它是否为空。
2.3 std::multimap 的构造函数与析构函数
构造函数
std::multimap 有多个构造函数,允许以不同的方式创建 multimap 对象。下面是一些常用的构造函数:
- std::multimap<K,V> s(): 默认构造函数,创建一个空的 multimap。
- std::multimap<K,V> s(const std::multimap<K,V>& other): 拷贝构造函数,创建一个与另一个 multimap 相同的 multimap。
- std::multimap<K,V> s(std::multimap<K,V>&& other);: 移动构造函数,创建一个与 other 内容相同的 multimap,并将 other 置于有效但未定义的状态(C++11 新加入)。
- std::multimap<K,V> s(std::initializer_list<K,V> init);: 使用初始化列表来创建 multimap(C++11 新加入)。
析构函数
- ~multimap(): 析构函数,释放 multimap 中的所有元素。
如下为样例代码:
#include <iostream>
#include <map>
#include <string>
int main()
{
// 默认构造函数,创建一个空的 multimap
std::multimap<std::string, int> mapDefault;
std::cout << "mapDefault is empty: " << mapDefault.empty() << std::endl;
// 插入元素到mapDefault
mapDefault.insert(std::make_pair("Apple", 1));
mapDefault.insert(std::make_pair("Apple", 2));
mapDefault.insert(std::make_pair("Banana", 3));
mapDefault.insert(std::make_pair("Banana", 4));
// 拷贝构造函数,创建一个与mapDefault相同的 multimap
std::multimap<std::string, int> mapCopy(mapDefault);
std::cout << "mapCopy contains " << mapCopy.size() << " elements." << std::endl;
// 移动构造函数,创建一个与 mapCopy 内容相同的 multimap,并将 mapCopy 置于有效但未定义状态
std::multimap<std::string, int> mapMove(std::move(mapCopy));
std::cout << "mapMove contains " << mapMove.size() << " elements." << std::endl;
// mapCopy 已经移动到了 mapMove,所以现在是空的
std::cout << "mapCopy is now empty: " << mapCopy.size() << std::endl;
// 使用初始化列表创建map
std::multimap<std::string, int> mapInitList = {
{"Apple", 4},
{"Apple", 5},
{"Banana", 6},
{"Banana", 7}
};
std::cout << "mapInitList contains " << mapInitList.size() << " elements." << std::endl;
// 析构函数会在 multimap 对象离开作用域时自动调用
// 在这个例子中,当 main 函数结束时,所有map对象都会被析构
// 注意:我们没有直接调用析构函数,它是隐式调用的。
return 0;
}
上面代码的输出为:
mapDefault is empty: 1
mapCopy contains 4 elements.
mapMove contains 4 elements.
mapCopy is now empty: 0
mapInitList contains 4 elements.
在这个示例中,分别展示了 std::multimap 的默认构造函数、拷贝构造函数、移动构造函数以及使用初始化列表创建 multimap 的方法。然后,通过检查 empty() 方法和 size() 方法验证了 multimap 对象的状态。需要注意的是,当使用移动构造函数后,原来的 multimap 对象(在这个例子中是 mapCopy )将不再拥有其原有数据,并且处于有效但未定义的状态。
析构函数在这个示例中并没有显式调用,因为当 multimap 对象离开其作用域时,它们会自动被析构。在实际程序中,析构函数用于清理 multimap 对象所占用的资源,但通常不需要显式调用它,因为 C++ 会自动管理对象的生命周期。
3 std::multimap 的元素操作
3.1 std::multimap 元素的访问与修改
3.1.1 访问元素
使用迭代器
可以使用迭代器来遍历 std::multimap 中的所有元素。由于 std::multimap 中的元素是按照键的升序排列的,因此迭代器会按照排序顺序访问元素。
#include <iostream>
#include <map>
int main()
{
std::multimap<int, std::string> myMultimap;
myMultimap.insert({1, "apple"});
myMultimap.insert({2, "banana"});
myMultimap.insert({1, "cherry"});
// 使用迭代器遍历所有元素
for (const auto& pair : myMultimap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
// 使用迭代器手动遍历
for (std::multimap<int, std::string>::const_iterator it = myMultimap.begin();
it != myMultimap.end(); ++it) {
std::cout << it->first << ": " << it->second << std::endl;
}
return 0;
}
使用 find 方法
如果需要根据特定的键来查找元素,可以使用 find 方法。find 方法会返回一个指向找到的第一个元素的迭代器,如果没有找到则返回 end()。
auto it = myMultimap.find(1);
if (it != myMultimap.end()) {
std::cout << "Found element with key 1: " << it->second << std::endl;
} else {
std::cout << "Element with key 1 not found." << std::endl;
}
使用 lower_bound 和 upper_bound 方法
由于 std::multimap 允许键重复,所以有时需要想要找到所有具有特定键的元素。这可以通过结合使用 lower_bound 和 upper_bound 方法来实现。
auto lower = myMultimap.lower_bound(1);
auto upper = myMultimap.upper_bound(1);
// 遍历所有键为 1 的元素
for (auto it = lower; it != upper; ++it) {
std::cout << it->first << ": " << it->second << std::endl;
}
这里,lower_bound 返回指向第一个键不小于给定键的元素的迭代器,而 upper_bound 返回指向第一个键大于给定键的元素的迭代器。因此,通过在这两个迭代器之间进行迭代,可以访问所有具有特定键的元素。
使用 count 方法
如果需要知道具有特定键的元素有多少个,可以使用 count 方法。
size_t count = myMultimap.count(1);
std::cout << "Number of elements with key 1: " << count << std::endl;
这些方法提供了灵活的方式来访问和操作 std::multimap 中的元素。根据具体需求,可以选择最适合的方法。
3.1.2 修改元素
在 std::multimap 中,一旦元素被插入,就不能直接修改元素的键(key),因为这会破坏容器的排序特性。但是,可以修改元素的值(value)部分。如果需要修改键,则必须删除原有的元素并插入一个新的元素。
下面是如何修改 std::multimap 中元素的值:
#include <iostream>
#include <map>
#include <string>
int main()
{
std::multimap<int, std::string> myMultimap;
myMultimap.insert({1, "apple"});
myMultimap.insert({2, "banana"});
myMultimap.insert({1, "cherry"});
// 查找键为 1 的第一个元素,并修改其值
auto it = myMultimap.find(1);
if (it != myMultimap.end()) {
it->second = "new_value"; // 修改值部分
std::cout << "Value modified to: " << it->second << std::endl;
} else {
std::cout << "Element with key 1 not found." << std::endl;
}
// 遍历并打印修改后的元素
for (const auto& pair : myMultimap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
在这个例子中,找到了键为 1 的第一个元素,并将其值从 “apple” 修改为 “new_value”。注意,由于 std::multimap 允许键重复,所以可能需要遍历所有键为 1 的元素来修改它们的值。
如果需要修改键,则必须删除原有的元素并插入一个新的元素。例如:
// 假设要将键从 1 修改为 3
auto range = myMultimap.equal_range(1); // 获取所有键为 1 的元素范围
for (auto it = range.first; it != range.second; ) {
auto next = std::next(it); // 保存下一个迭代器的位置,因为 erase 会使当前迭代器失效
std::pair<int, std::string> new_pair(3, it->second); // 创建新键值对,键为 3
myMultimap.insert(new_pair); // 插入新元素
myMultimap.erase(it); // 删除原元素
it = next; // 移动到下一个元素
}
请注意,在遍历并删除元素时,必须小心处理迭代器,因为 erase 操作会使指向被删除元素的迭代器失效。上面的代码中,在调用 erase 之前保存了下一个迭代器的位置,以确保可以安全地继续遍历。
3.2 std::multimap 元素的插入操作
std::multimap 中与元素插入操作相关的方法有如下几种:
- insert(const value_type& val): 插入一个元素。
- insert(value_type&& val): 移动插入一个元素(C++11 及以后)。
- insert(input_iterator first, input_iterator last): 插入一个元素范围。
如下为样例代码:
#include <iostream>
#include <map>
#include <string>
#include <vector>
int main()
{
std::multimap<int, std::string> myMultimap;
// 使用 insert(const value_type& val) 插入一个元素
myMultimap.insert(std::make_pair(1, "apple"));
// 使用 insert(value_type&& val) 移动插入一个元素 (C++11 及以后)
myMultimap.insert(std::move(std::make_pair(1, "banana")));
// 使用 insert(input_iterator first, input_iterator last) 插入一个元素范围
std::vector<std::pair<int, std::string>> elements = { {2, "elderberry"}, {2, "fig"} };
myMultimap.insert(elements.begin(), elements.end());
// 遍历并打印所有元素
for (const auto& pair : myMultimap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
上面代码的输出为:
1: apple
1: banana
2: elderberry
2: fig
这个样例首先使用 insert 的不同形式向 std::multimap 中插入元素。注意,当使用右值引用(std::move)进行移动插入,这通常可以提高性能,因为它避免了不必要的复制操作。
然后使用了范围插入来一次性插入多个元素,这些元素来自一个 std::vector,其中包含预定义的键值对。
3.3 std::multimap 元素的删除操作
std::multimap 中与元素删除操作相关的方法有如下几种:
- erase(const key_type& k);: 删除键为 k 的元素。
- erase(const_iterator position);: 删除指定位置的元素。
- erase(const_iterator first, const_iterator last);: 删除一个元素范围。
- clear();: 删除 multimap 中的所有元素。
如下为样例代码:
#include <iostream>
#include <map>
#include <string>
int main()
{
std::multimap<int, std::string> myMultimap;
// 插入一些元素
myMultimap.insert({ 1, "apple" });
myMultimap.insert({ 2, "banana" });
myMultimap.insert({ 1, "cherry" });
myMultimap.insert({ 3, "date" });
myMultimap.insert({ 2, "elderberry" });
// 遍历并打印原始元素
std::cout << "Original elements:" << std::endl;
for (const auto& pair : myMultimap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
// 使用 erase(const key_type& k) 删除键为 2 的所有元素
size_t count = myMultimap.erase(2);
std::cout << "Erased " << count << " elements with key 2." << std::endl;
// 遍历并打印剩余元素
std::cout << "Elements after erasing key 2:" << std::endl;
for (const auto& pair : myMultimap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
// 使用 erase(const_iterator position) 删除第一个元素
auto it = myMultimap.begin();
myMultimap.erase(it);
std::cout << "Erased first element." << std::endl;
// 遍历并打印剩余元素
std::cout << "Elements after erasing first element:" << std::endl;
for (const auto& pair : myMultimap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
// 使用 erase(const_iterator first, const_iterator last) 删除剩余所有元素
it = myMultimap.begin();
myMultimap.erase(it, myMultimap.end());
std::cout << "Erased all remaining elements." << std::endl;
// 验证 multimap 是否为空
if (myMultimap.empty()) {
std::cout << "Multimap is empty." << std::endl;
}
else {
std::cout << "Multimap is not empty." << std::endl;
}
// 使用 clear() 删除所有元素
myMultimap.clear();
std::cout << "Cleared the multimap." << std::endl;
// 再次验证 multimap 是否为空
if (myMultimap.empty()) {
std::cout << "Multimap is empty after clear." << std::endl;
}
else {
std::cout << "Multimap is not empty after clear." << std::endl;
}
return 0;
}
上面代码的输出为:
Original elements:
1: apple
1: cherry
2: banana
2: elderberry
3: date
Erased 2 elements with key 2.
Elements after erasing key 2:
1: apple
1: cherry
3: date
Erased first element.
Elements after erasing first element:
1: cherry
3: date
Erased all remaining elements.
Multimap is empty.
Cleared the multimap.
Multimap is empty after clear.
在在这个样例中,首先插入了一些元素到 std::multimap 中。然后使用不同的 erase 方法来删除元素:
- 使用 erase(const key_type& k) 删除键为 2 的所有元素。
- 使用 erase(const_iterator position) 删除第一个元素。
- 使用 erase(const_iterator first, const_iterator last) 删除剩余的所有元素。
在每个删除操作之后,都遍历并打印剩余的元素,以展示 multimap 的当前状态。
3.4 std::multimap 元素的查找操作
std::multimap 中与查找操作的方法有如下几种:
- find(const key_type& k);: 查找键为 k 的元素,并返回指向它的迭代器,否则返回 end()。
- count(const key_type& k);: 返回键为 k 的元素的数量。
- lower_bound(const key_type& k);: 返回指向第一个不小于 k 的元素的迭代器。
- upper_bound(const key_type& k);: 返回指向第一个大于 k 的元素的迭代器。
- equal_range(const key_type& k);: 返回一个包含键为 k 的所有元素的迭代器对。
如下为样例代码:
#include <iostream>
#include <map>
#include <string>
int main()
{
std::multimap<int, std::string> myMultimap;
// 插入一些元素
myMultimap.insert({ 1, "apple" });
myMultimap.insert({ 2, "banana" });
myMultimap.insert({ 1, "cherry" });
myMultimap.insert({ 3, "date" });
myMultimap.insert({ 2, "elderberry" });
// 使用 find 查找键为 2 的元素
auto it = myMultimap.find(2);
if (it != myMultimap.end()) {
std::cout << "Found " << it->first << ": " << it->second << std::endl;
}
else {
std::cout << "Key 2 not found." << std::endl;
}
// 使用 count 查找键为 1 的元素数量
size_t count = myMultimap.count(1);
std::cout << "There are " << count << " elements with key 1." << std::endl;
// 使用 lower_bound 查找第一个不小于 2 的元素
it = myMultimap.lower_bound(2);
if (it != myMultimap.end()) {
std::cout << "First element not less than 2: " << it->first << ": " << it->second << std::endl;
}
else {
std::cout << "No elements not less than 2." << std::endl;
}
// 使用 upper_bound 查找第一个大于 2 的元素
it = myMultimap.upper_bound(2);
if (it != myMultimap.end()) {
std::cout << "First element greater than 2: " << it->first << ": " << it->second << std::endl;
}
else {
std::cout << "No elements greater than 2." << std::endl;
}
// 使用 equal_range 查找键为 1 的所有元素
auto range = myMultimap.equal_range(1);
for (auto it = range.first; it != range.second; ++it) {
std::cout << "Element with key 1: " << it->first << ": " << it->second << std::endl;
}
return 0;
}
上面代码的输出为:
Found 2: banana
There are 2 elements with key 1.
First element not less than 2: 2: banana
First element greater than 2: 3: date
Element with key 1: 1: apple
Element with key 1: 1: cherry
在这个样例中,首先向 std::multimap 中插入了一些元素。然后使用不同的查找方法来获取关于这些元素的信息:
- 使用 find 方法查找键为 2 的元素,并打印找到的元素。
- 使用 count 方法计算键为 1 的元素数量,并打印结果。
- 使用 lower_bound 方法查找第一个不小于 2 的元素,并打印找到的元素。
- 使用 upper_bound 方法查找第一个大于 2 的元素,并打印找到的元素。
- 使用 equal_range 方法获取键为 1 的所有元素的迭代器范围,并遍历打印这些元素。
这个样例展示了如何使用 std::multimap 的查找方法来检索和遍历具有特定键的元素。注意,由于 std::multimap 允许键重复,因此 count 方法对于具有重复键的元素将返回大于 1 的值,而 equal_range 方法返回的范围可能包含多个元素。
3.5 std::multimap 元素的遍历删除
如果需要在遍历过程中逐个删除元素,可以使用 std::multimap::erase 方法结合普通的循环,但每次删除元素后,都需要更新迭代器。以下是一个逐个删除特定元素的例子:
如下为样例代码:
#include <iostream>
#include <map>
int main()
{
std::multimap<int, int> vals = { {1,2}, {2,3}, {2,4}, {4,5}, {6,6} };
auto it = vals.begin();
while (it != vals.end()) {
if (it->first % 2 == 0) {
it = vals.erase(it); // erase 返回下一个有效元素的迭代器
}
else {
++it; // 继续到下一个元素
}
}
// 输出结果
for (const auto& elem : vals) {
std::cout << elem.first << ":" << elem.second << std::endl;
}
return 0;
}
上面代码的输出为:
1:2
在上面代码中,使用一个循环来遍历 multimap,并在每次迭代中检查当前元素是否满足删除条件。如果满足条件,则使用 erase 方法删除该元素,并更新迭代器。如果不满足条件,则简单地递增迭代器以继续遍历。
注意:在删除元素后,迭代器 it 会被 erase 方法更新为指向被删除元素之后的位置,因此在下一次循环迭代中,it 仍然有效。
4 std::multimap 的迭代器
4.1 std::multimap 迭代器的基本使用
std::multimap 中与迭代器相关的方法有如下几种:
- begin(): 返回一个指向容器中第一个元素的迭代器。
- end(): 返回一个指向容器中最后一个元素之后位置的迭代器。
- rbegin(): 返回一个指向容器中最后一个元素的反向迭代器。
- rend(): 返回一个指向容器中第一个元素之前位置的反向迭代器。
- cbegin(), cend(), crbegin(), crend(): 与上述类似,但返回的是常量迭代器或常量反向迭代器。
如下为样例代码:
#include <iostream>
#include <map>
#include <string>
int main()
{
std::multimap<int, std::string> myMultimap;
// 插入一些元素
myMultimap.insert({ 1, "apple" });
myMultimap.insert({ 2, "banana" });
myMultimap.insert({ 1, "cherry" });
myMultimap.insert({ 3, "date" });
myMultimap.insert({ 2, "elderberry" });
// 使用 begin 和 end 遍历所有元素
for (auto it = myMultimap.begin(); it != myMultimap.end(); ++it) {
std::cout << "Element: " << it->first << ": " << it->second << std::endl;
}
// 使用 cbegin 和 cend 遍历所有元素(常量迭代器)
for (auto it = myMultimap.cbegin(); it != myMultimap.cend(); ++it) {
std::cout << "Constant Element: " << it->first << ": " << it->second << std::endl;
}
// 使用 rbegin 和 rend 反向遍历所有元素
for (auto it = myMultimap.rbegin(); it != myMultimap.rend(); ++it) {
std::cout << "Reverse Element: " << it->first << ": " << it->second << std::endl;
}
// 使用 crbegin 和 crend 反向遍历所有元素(常量迭代器)
for (auto it = myMultimap.crbegin(); it != myMultimap.crend(); ++it) {
std::cout << "Constant Reverse Element: " << it->first << ": " << it->second << std::endl;
}
// 使用迭代器直接访问第一个和最后一个元素
if (!myMultimap.empty()) {
std::cout << "First Element: " << myMultimap.begin()->first << ": " << myMultimap.begin()->second << std::endl;
std::cout << "Last Element: " << myMultimap.rbegin()->first << ": " << myMultimap.rbegin()->second << std::endl;
}
return 0;
}
上面代码的输出为:
Element: 1: apple
Element: 1: cherry
Element: 2: banana
Element: 2: elderberry
Element: 3: date
Constant Element: 1: apple
Constant Element: 1: cherry
Constant Element: 2: banana
Constant Element: 2: elderberry
Constant Element: 3: date
Reverse Element: 3: date
Reverse Element: 2: elderberry
Reverse Element: 2: banana
Reverse Element: 1: cherry
Reverse Element: 1: apple
Constant Reverse Element: 3: date
Constant Reverse Element: 2: elderberry
Constant Reverse Element: 2: banana
Constant Reverse Element: 1: cherry
Constant Reverse Element: 1: apple
First Element: 1: apple
Last Element: 3: date
这个样例展示了如何使用不同类型的迭代器来遍历 std::multimap。begin() 和 end() 用于正向遍历,rbegin() 和 rend() 用于反向遍历。同时,cbegin(), cend(), crbegin(), 和 crend() 是常量版本的迭代器,它们用于保证在遍历过程中不会修改容器中的元素。
4.2 std::multimap 迭代器使用的注意事项
在使用 std::multimap 的迭代器时,有几个重要的注意事项需要牢记:
(1)有效性: 一旦迭代器指向的元素被删除或移动,迭代器就失效了。在删除或修改 std::multimap 中的元素后,必须确保不再使用失效的迭代器。
(2)常量迭代器: std::multimap 提供了常量迭代器(const_iterator),这些迭代器不能用来修改元素的值。
(3)遍历过程中删除元素: 在遍历 std::multimap 的过程中直接删除元素会导致迭代器失效。如果需要删除元素,通常的做法是使用一个单独的容器(如 std::vector 或另一个 std::multimap)来保存要删除的键,然后在遍历结束后使用 erase 方法删除这些键。
(4)end() 方法返回的迭代器: end() 方法返回的迭代器指向的是容器中的“尾后”位置,即最后一个元素之后的位置。这个迭代器不能被解引用。在遍历容器时,应该小心不要试图访问 end() 返回的迭代器。
(5)修改键值: std::multimap 中的元素是根据键值排序的。如果通过迭代器修改了元素的键值,这将会破坏容器的排序特性。通常情况下,不应该这样做(实际上这么做也无法通过编译)。如果需要改变元素的键值,应该先删除原元素,然后插入新的元素。