本文主要介绍C++ STL常用容器之map(关联容器)的相关概念以及用法。
std::map 是C++标准模板库(STL)中的一个关联容器,它包含不重复的键值对(key-value pairs)集合,其中每个键都是唯一的。map的内部使用一个红黑树进行存储,因此能够保持元素的有序性,并提供高效的查找、插入和删除操作。
优点 | 描述 |
---|---|
快速查找 | 由于map的内部使用红黑树作为数据结构,因此查找、插入和删除操作的时间复杂度为对数级别,非常高效。 |
唯一性保证 | map 保证每个键在容器中都是唯一的,不会出现重复键。 |
支持复杂数据类型 | map的键和值可以是任意数据类型,包括自定义类型。 |
动态调整 | 插入和删除操作会自动调整内部结构以保持有序性和平衡性。 |
缺点 | 描述 |
---|---|
空间开销较大 | 由于map的内部使用红黑树来维护元素的有序性和唯一性,因此需要额外的空间来存储树的节点信息。 |
性能开销 | 插入和删除操作的时间复杂度为 O(log n),对于某些高性能要求的应用来说,可能不如哈希表(如 unordered_map)高效。 |
map常用于需要快速根据键来查找、插入或删除值的情况。例如:
使用场景 | 描述 |
---|---|
有序存储 | 当需要按键的顺序进行遍历或查找时,map 是理想的选择。例如,存储需要按日期排序的日志记录。 |
快速查找 | 需要在大量数据中快速查找某个键对应的值时,map 可以提供高效的查找能力。 |
键值对存储 | 需要存储一些关联关系的数据,如学生 ID 和学生信息的关联、单词和其出现频率的关联等。 |
唯一键要求 | 需要确保每个键是唯一的场景,如用户 ID 映射到用户信息的存储。 |
#include
#include
int main(int argc, char* argv[])
{
// 使用默认构造函数创建一个空的map
std::map<int, std::string> myMap;
// 使用列表初始化创建并初始化map
std::map<int, std::string> myMap2 = {{1, "one"}, {2, "two"}, {3, "three"}};
// 使用auto遍历
for (const auto &pair : myMap2) {
std::cout << "Key: " << pair.first << ", Value: " << pair.second << std::endl;
}
// 使用迭代器遍历
// for (std::map::iterator it = myMap2.begin(); it != myMap2.end(); it++) {
// std::cout << "Key: " << it->first << ", Value: " << it->second << std::endl;
// }
return 0;
}
编译后输出如下
jeff@jeff:/tmp$ g++ -o main main.cpp
jeff@jeff:/tmp$ ./main
Key: 1, Value: one
Key: 2, Value: two
Key: 3, Value: three
jeff@jeff:/tmp$
#include
#include
int main(int argc, char* argv[])
{
// 使用默认构造函数创建一个空的map
std::map<int, std::string> myMap;
std::cout << "myMap.size:" << myMap.size() << std::endl;
// 使用列表初始化创建并初始化map
std::map<int, std::string> myMap2 = {{1, "one"}, {2, "two"}, {3, "three"}};
std::cout << "myMap2.size:" << myMap2.size() << std::endl;
return 0;
}
编译后输出如下
jeff@jeff:/tmp$ g++ -o main main.cpp
jeff@jeff:/tmp$ ./main
myMap.size:0
myMap2.size:3
jeff@jeff:/tmp$
#include
#include
int main(int argc, char* argv[])
{
// 使用列表初始化创建并初始化map
std::map<int, std::string> myMap = {{1, "one"}, {2, "two"}, {3, "three"}};
// 使用 at() 访问
std::cout << "Key 1: " << myMap.at(1) << std::endl;
// 使用 [] 操作符
std::cout << "Key 2: " << myMap[2] << std::endl;
// 使用 find()
auto it = myMap.find(3);
if (it != myMap.end()) {
std::cout << "Key 3: " << it->second << std::endl;
}
return 0;
}
编译后输出如下
jeff@jeff:/tmp$ g++ -o main main.cpp
jeff@jeff:/tmp$ ./main
Key 1: one
Key 2: two
Key 3: three
jeff@jeff:/tmp$
#include
#include
int main(int argc, char* argv[])
{
// 使用列表初始化创建并初始化map
std::map<int, std::string> myMap = {{1, "one"}, {2, "two"}, {3, "three"}};
// 使用 insert()
myMap.insert(std::make_pair(4, "four"));
// 使用 [] 操作符
myMap[5] = "five";
for (const auto& pair : myMap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
编译后输出如下
jeff@jeff:/tmp$ g++ -o main main.cpp
jeff@jeff:/tmp$ ./main
1: one
2: two
3: three
4: four
5: five
jeff@jeff:/tmp$
#include
#include
int main(int argc, char* argv[])
{
// 使用列表初始化创建并初始化map
std::map<int, std::string> myMap = {{1, "one"}, {2, "two"}, {3, "three"}, {4, "four"}, {5, "five"}};
std::cout << "before erase" << std::endl;
for (const auto& pair : myMap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
// 通过键删除元素
myMap.erase(1);
// 通过迭代器删除元素
auto it = myMap.find(4);
if (it != myMap.end()) {
myMap.erase(it);
std::cout << "after erase" << std::endl; }
for (const auto& pair : myMap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
编译后输出如下
jeff@jeff:/tmp$ ./main
before erase
1: one
2: two
3: three
4: four
5: five
after erase
2: two
3: three
5: five
jeff@jeff:/tmp$
std::map<int, std::string> myMap = {{1, "one"}, {2, "two"}, {3, "three"}};
// 清空容器
myMap.clear();
std::cout << "Size after clearing: " << myMap.size() << std::endl;
std::map 在C++ STL(标准模板库)中的底层实现通常依赖于红黑树(Red-Black Tree)这种数据结构。红黑树是一种特殊的自平衡二叉搜索树,它通过颜色和一系列的调整规则来保持树的平衡性,从而保证了高效的查找、插入和删除操作。
红黑树通过颜色(红色或黑色)和一系列旋转操作来维持树的平衡,确保在最坏情况下查找、插入和删除操作的时间复杂度为O(log n),其中n是树中节点的数量。
总的来说,红黑树在std::map中的应用为C++程序员提供了一种高效、有序且稳定的键值对存储方式。