🔗 运行环境:Matlab
🚩 撰写作者:左手の明天
🥇 精选专栏:《python》
🔥 推荐专栏:《算法研究》
🔐#### 防伪水印——左手の明天 ####🔐
💗 大家好🤗🤗🤗,我是左手の明天!好久不见💗
💗今天分享C/C++——关联容器map的使用💗
📆 最近更新:2024 年 05 月 26 日,左手の明天的第 331 篇原创博客
📚 更新于专栏:C/C++入门与进阶
🔐#### 防伪水印——左手の明天 ####🔐
一、引言
关联容器map是STL(Standard Template Library,标准模板库)中的一个重要组件,它提供了一对一的数据处理能力。在map中,每个元素都是一个关键字-值(key-value)对,其中关键字起到索引的作用,而值则表示与索引相关联的数据。这种特性使得map在处理一对一数据时,能够在编程上提供快速通道。
map内部自建一棵红黑树(一种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,因此map内部的所有数据都是有序的。这种有序性为数据的查找、插入和删除等操作提供了便利。
在map中,关键字是唯一的,每个关键字在map中只能出现一次。而值是可以修改的,可以通过关键字来访问和修改与之对应的值。此外,map还支持下标访问符,通过在[]中放入关键字,可以找到与该关键字对应的值。
总的来说,关联容器map是一个功能强大的数据结构,它能够在处理一对一数据时提供高效的数据处理能力,并在编程中提供便捷的操作方式。
二、Map的构造函数
map的构造函数有多种形式,包括匿名对象构造、有名构造、多参数构造函数的隐式类型转换等。在插入数据时,可以使用insert函数插入pair数据或value_type数据,也可以使用数组方式插入数据。需要注意的是,当使用insert函数插入数据时,如果map中已存在相同的关键字,则插入操作不会生效;而使用数组方式插入数据时,如果关键字已存在,则会覆盖以前该关键字对应的值。
std::map
在C++标准库中有多个构造函数,允许你以不同的方式初始化map
对象。以下是一些常见的std::map
构造函数及其用法:
2.1 默认构造函数
默认构造函数创建一个空的map
。
std::map<int, std::string> myMap;
2.2 复制构造函数
复制构造函数允许你从一个已存在的map
对象创建一个新的map
对象。
std::map<int, std::string> myMap1;
myMap1[1] = "one";
myMap1[2] = "two";std::map<int, std::string> myMap2(myMap1); // 使用myMap1初始化myMap2
2.3 初始化列表构造函数
使用初始化列表可以直接在构造函数中初始化map
的元素。
std::map<int, std::string> myMap = {{1, "one"},{2, "two"},{3, "three"}
};
或者C++11及以后的语法:
std::map<int, std::string> myMap{{1, "one"},{2, "two"},{3, "three"}
};
2.4 迭代器范围的构造函数
你可以使用一对迭代器来从一个已存在的容器(如数组、另一个map
、vector
等)初始化map
。
std::pair<int, std::string> array[] = {{1, "one"},{2, "two"},{3, "three"}
};std::map<int, std::string> myMap(array, array + sizeof(array) / sizeof(array[0]));
2.5 分配器构造函数
分配器构造函数允许你使用一个自定义的分配器来管理map
的内存。这在需要控制内存分配和释放时特别有用。
std::allocator<std::pair<const int, std::string>> alloc;
std::map<int, std::string> myMap(alloc);
在实际使用中,你通常会使用默认构造函数或初始化列表构造函数来创建和初始化map
对象。例如,当你需要快速创建一个具有固定键值对的map
时,初始化列表构造函数非常方便。
记住,由于
map
是有序的,所以当你使用初始化列表或迭代器范围构造函数时,键值对的顺序将影响最终map
中元素的顺序。如果你使用默认的比较函数(std::less<Key>
),元素将按照键的升序排列。如果你提供了自定义的比较函数,元素将根据该比较函数的逻辑排序。
三、Map的基本操作函数
C++ Maps是一种关联式容器,包含“关键字/值”对,如下表:
操作函数 | 含义 |
begin() | 返回指向map头部的迭代器 |
clear() | 删除所有元素 |
count() | 返回指定元素出现的次数 |
empty() | 如果map为空则返回true |
end() | 返回指向map末尾的迭代器 |
equal_range() | 返回特殊条目的迭代器对 |
erase() | 删除一个元素 |
find() | 查找一个元素 |
get_allocator() | 返回map的配置器 |
insert() | 插入元素 |
key_comp() | 返回比较元素key的函数 |
lower_bound() | 返回键值>=给定元素的第一个位置 |
max_size() | 返回可以容纳的最大元素个数 |
rbegin() | 返回一个指向map尾部的逆向迭代器 |
rend() | 返回一个指向map头部的逆向迭代器 |
size() | 返回map中元素的个数 |
swap() | 交换两个map |
value_comp() | 返回比较元素value的函数 |
upper_bound() | 返回键值>给定元素的第一个位置 |
四、Map的实现
Map是存储键值对的数据结构,它提供了一种方便的方法来访问和操作这些键值对。在C语言中,可以使用哈希表来实现Map。Map提供了以下基本操作:
- 插入:将一个新的键值对插入到Map中。
- 查找:根据键查找对应的值。
- 删除:删除指定的键值对。
- 更新:修改指定键的值。
- 大小:返回Map中存储的键值对的数量。
- 遍历:遍历Map中的所有键值对。
在C++中,std::map
是一个关联容器,它存储的元素都是键值对(key-value),并且根据键的值自动排序。每个键在map
中只能出现一次,键不能修改,但对应的值可以修改。map
的底层结构通常是用红黑树实现的,因此map
中的元素总是有序的。
下面是使用std::map
的基本步骤:
4.1 包含头文件
首先,你需要包含<map>
头文件来使用std::map
。
#include <map>
4.2 定义和创建map对象
你可以使用std::map
模板来定义和创建map
对象。你需要指定键和值的类型。
std::map<int, std::string> myMap; // 定义一个map,键是int类型,值是std::string类型
4.3 插入元素
你可以使用insert()
函数来插入元素。通常,你可以使用std::make_pair
来创建一个键值对,并将其插入到map
中。
myMap.insert(std::make_pair(1, "one"));
myMap.insert(std::make_pair(2, "two"));
或者,你可以直接使用[]
运算符来插入或修改元素(如果键已存在,则修改对应的值;如果键不存在,则插入新的键值对)。
myMap[3] = "three"; // 插入键为3,值为"three"的键值对
4.4 访问元素
你可以使用[]
运算符或at()
函数来访问元素。如果键不存在于map
中,at()
会抛出异常,而[]
会创建一个新的元素。
std::string value = myMap[1]; // 访问键为1的元素的值
std::string value2 = myMap.at(2); // 另一种访问元素的方式
4.5 遍历map
你可以使用迭代器来遍历map
中的所有元素。
for (std::map<int, std::string>::iterator it = myMap.begin(); it != myMap.end(); ++it) {std::cout << "Key: " << it->first << ", Value: " << it->second << std::endl;
}
或者使用基于范围的for循环(C++11及以后版本):
for (const auto& kv : myMap) {std::cout << "Key: " << kv.first << ", Value: " << kv.second << std::endl;
}
4.6 删除元素
你可以使用erase()
函数来删除元素。
myMap.erase(1); // 删除键为1的元素
4.7 查找元素
你可以使用find()
函数来检查元素是否存在。find()函数返回一个迭代器指向键值为key的元素,如果没找到就返回指向map尾部的迭代器。
auto it = myMap.find(1);
if (it != myMap.end()) {std::cout << "Element found with key 1." << std::endl;
}
else {std::cout << "Element not found." << std::endl;
}
这些是std::map
的基本用法。在实际编程中,你可能还需要了解更多关于map
的高级用法,比如自定义比较函数、使用emplace()
函数插入元素等。你可以查阅C++标准库文档或相关教程来获取更多信息。
4.8 map中swap的用法:
Map中的swap不是一个容器中的元素交换,而是两个容器交换;
#include <map>
#include <iostream>using namespace std;int main( )
{map <int, int> m1, m2, m3;map <int, int>::iterator m1_Iter;m1.insert ( pair <int, int> ( 1, 10 ) );m1.insert ( pair <int, int> ( 2, 20 ) );m1.insert ( pair <int, int> ( 3, 30 ) );m2.insert ( pair <int, int> ( 10, 100 ) );m2.insert ( pair <int, int> ( 20, 200 ) );m3.insert ( pair <int, int> ( 30, 300 ) );cout << "The original map m1 is:";for ( m1_Iter = m1.begin( ); m1_Iter != m1.end( ); m1_Iter++ )cout << " " << m1_Iter->second;cout << "." << endl;// This is the member function version of swap//m2 is said to be the argument map; m1 the target mapm1.swap( m2 );cout << "After swapping with m2, map m1 is:";for ( m1_Iter = m1.begin( ); m1_Iter != m1.end( ); m1_Iter++ )cout << " " << m1_Iter -> second;cout << "." << endl;cout << "After swapping with m2, map m2 is:";for ( m1_Iter = m2.begin( ); m1_Iter != m2.end( ); m1_Iter++ )cout << " " << m1_Iter -> second;cout << "." << endl;// This is the specialized template version of swapswap( m1, m3 );cout << "After swapping with m3, map m1 is:";for ( m1_Iter = m1.begin( ); m1_Iter != m1.end( ); m1_Iter++ )cout << " " << m1_Iter -> second;cout << "." << endl;
}
4.9 map的sort问题:
Map中的元素是自动按key升序排序,所以不能对map用sort函数:
#include <map>
#include <iostream>using namespace std;int main( )
{map <int, int> m1;map <int, int>::iterator m1_Iter;m1.insert ( pair <int, int> ( 1, 20 ) );m1.insert ( pair <int, int> ( 4, 40 ) );m1.insert ( pair <int, int> ( 3, 60 ) );m1.insert ( pair <int, int> ( 2, 50 ) );m1.insert ( pair <int, int> ( 6, 40 ) );m1.insert ( pair <int, int> ( 7, 30 ) );cout << "The original map m1 is:"<<endl;for ( m1_Iter = m1.begin( ); m1_Iter != m1.end( ); m1_Iter++ )cout << m1_Iter->first<<" "<<m1_Iter->second<<endl;
}
The original map m1 is:
1 20
2 50
3 60
4 40
6 40
7 30
五、Map定义自定义比较函数
在std::map
中定义自定义比较函数是通过向std::map
模板的第三个参数传递一个比较对象来实现的。这个比较对象通常是一个函数对象(也称为仿函数)或者Lambda表达式,它需要重载operator()
来定义两个元素之间的比较逻辑。
下面是使用自定义比较函数的std::map
的几种方式:
5.1 使用函数对象(仿函数)
首先,定义一个比较函数对象:
struct MyComparator {bool operator()(const int& a, const int& b) const {// 定义比较逻辑,这里假设我们要按照降序排列return a > b;}
};
然后,使用这个比较函数对象来创建std::map
:
std::map<int, std::string, MyComparator> myMap;
5.2 使用Lambda表达式(C++11及以后)
在C++11及更高版本中,你可以使用Lambda表达式作为比较函数:
auto comp = [](const int& a, const int& b) {// 定义比较逻辑return a > b;
};std::map<int, std::string, decltype(comp)> myMap(comp);
注意这里使用了decltype(comp)
来自动推断比较函数的类型。
5.3 使用标准库函数对象
有时,你可能不需要完全自定义比较逻辑,而是想要对map
中的键进行某种转换后再比较。在这种情况下,你可以使用标准库提供的函数对象适配器,如std::greater<>
或std::less<>
,配合自定义的键转换函数。但是,这通常是通过自定义比较函数对象来实现的,其中内部使用这个转换函数。
示例:降序排列的std::map
假设想要一个按照键的降序排列的std::map
:
#include <iostream>
#include <map>
#include <string>int main() {// 使用自定义比较函数对象创建降序排列的mapstd::map<int, std::string, std::greater<int>> myMap;myMap[3] = "three";myMap[1] = "one";myMap[2] = "two";// 遍历map,显示键值对for (const auto& kv : myMap) {std::cout << "Key: " << kv.first << ", Value: " << kv.second << std::endl;}return 0;
}
在这个例子中,使用了std::greater<int>
作为比较函数对象,它会使std::map
按照键的降序排列。
记住,
std::map
的默认比较函数是std::less<Key>
,它会产生升序排列的map
。通过提供自定义的比较函数对象,你可以改变元素的排序方式。