1. C++中 map
和 set
的基础概念
键值对 :
template <class T1, class T2>
struct pair
{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair(): first(T1()), second(T2())
{}
pair(const T1& a, const T2& b): first(a), second(b)
{}
};
1.1 什么是 map
?——键值对存储的强大工具
在C++中,map
是一种常用的关联容器,它允许存储以键值对形式存在的数据。每个元素都由唯一的键和与该键相关的值组成。map
的特点是键的唯一性,任何两个元素不能拥有相同的键。这使得 map
在需要快速查找、插入和删除数据时非常有用,尤其是当需要根据某些标识符来存取数据时。
1. map 是关联容器,它按照特定的次序 ( 按照 key 来比较 ) 存储由键值 key 和值 value 组合而成的元素。2. 在 map 中,键值 key 通常用于排序和惟一地标识元素,而值 value 中存储与此键值 key 关联的内容。键值 key 和值 value 的类型可能不同,并且在 map 的内部, key 与 value 通过成员类型value_type 绑定在一起,为其取别名称为 pair: typedef pair<const key, T> value_type;3. 在内部, map 中的元素总是按照键值 key 进行比较排序的。4. map 中通过键值访问单个元素的速度通常比 unordered_map 容器慢,但 map 允许根据顺序对元素进行直接迭代 ( 即对 map 中的元素进行迭代时,可以得到一个有序的序列 ) 。5. map 支持下标访问符,即在 [] 中放入 key ,就可以找到与 key 对应的 value 。6. map 通常被实现为二叉搜索树 ( 更准确的说:平衡二叉搜索树 ( 红黑树 )) 。
map的模板参数说明
map的构造
map的迭代器
map的容量与元素访问
map中元素的修改
#include <string>
#include <map>
void TestMap()
{
map<string, string> m;
// 向map中插入元素的方式:
// 将键值对<"peach","桃子">插入map中,用pair直接来构造键值对
m.insert(pair<string, string>("peach", "桃子"));
// 将键值对<"peach","桃子">插入map中,用make_pair函数来构造键值对
m.insert(make_pair("banan", "香蕉"));
// 借用operator[]向map中插入元素
/*
operator[]的原理是:
用<key, T()>构造一个键值对,然后调用insert()函数将该键值对插入到map中
如果key已经存在,插入失败,insert函数返回该key所在位置的迭代器
如果key不存在,插入成功,insert函数返回新插入元素所在位置的迭代器
operator[]函数最后将insert返回值键值对中的value返回
*/
// 将<"apple", "">插入map中,插入成功,返回value的引用,将“苹果”赋值给该引
用结果,
m["apple"] = "苹果";
// key不存在时抛异常
//m.at("waterme") = "水蜜桃";
cout << m.size() << endl;
// 用迭代器去遍历map中的元素,可以得到一个按照key排序的序列
for (auto& e : m)
cout << e.first << "--->" << e.second << endl;
cout << endl;
// map中的键值对key一定是唯一的,如果key存在将插入失败
auto ret = m.insert(make_pair("peach", "桃色"));
if (ret.second)
cout << "<peach, 桃色>不在map中, 已经插入" << endl;
else
cout << "键值为peach的元素已经存在:" << ret.first->first << "--->"
<< ret.first->second <<" 插入失败"<< endl;
// 删除key为"apple"的元素
m.erase("apple");
if (1 == m.count("apple"))
cout << "apple还在" << endl;
else
cout << "apple被吃了" << endl;
1. map 中的的元素是键值对2. map 中的 key 是唯一的,并且不能修改3. 默认按照小于的方式对 key 进行比较4. map 中的元素如果用迭代器去遍历,可以得到一个有序的序列5. map 的底层为平衡搜索树 ( 红黑树 ) ,查找效率比较高 $O(log_2 N)$6. 支持 [] 操作符, operator[] 中实际进行插入查找。
1.2 什么是 set
?——集合操作的利器
set
是C++标准模板库(STL)中的另一个重要容器,它用于存储唯一的值。与 map
不同,set
只存储键,不存储关联值。set
保证所有元素的唯一性,且默认按照升序排序。这使得 set
在去重、集合操作以及快速查找元素是否存在等应用场景中非常有效。
1. set 是按照一定次序存储元素的容器2. 在 set 中,元素的 value 也标识它 (value 就是 key ,类型为 T) ,并且每个 value 必须是唯一的。set 中的元素不能在容器中修改 ( 元素总是 const) ,但是可以从容器中插入或删除它们。3. 在内部, set 中的元素总是按照其内部比较对象 ( 类型比较 ) 所指示的特定严格弱排序准则进行排序。4. set 容器通过 key 访问单个元素的速度通常比 unordered_set 容器慢,但它们允许根据顺序对子集进行直接迭代。5. set 在底层是用二叉搜索树 ( 红黑树 ) 实现的。注意:1. 与 map/multimap 不同, map/multimap 中存储的是真正的键值对 <key, value> , set 中只放value ,但在底层实际存放的是由 <value, value> 构成的键值对。2. set 中插入元素时,只需要插入 value 即可,不需要构造键值对。3. set 中的元素不可以重复 ( 因此可以使用 set 进行去重 ) 。4. 使用 set 的迭代器遍历 set 中的元素,可以得到有序序列5. set 中的元素默认按照小于来比较6. set 中查找某个元素,时间复杂度为: $log_2 n$7. set 中的元素不允许修改 ( 为什么 ?)8. set 中的底层使用二叉搜索树 ( 红黑树 ) 来实现
1.3 map
和 set
的主要区别
主要区别在于,map
是键值对形式的容器,而 set
仅存储值。map
通过键来快速定位值,而 set
则是通过值本身进行操作。此外,map
使用的键是唯一的,set
中的所有元素也必须唯一。
1.4 何时选择 map
,何时选择 set
如果需要存储键值对并根据键进行快速查找,那么 map
是合适的选择。如果只需要存储一组唯一的值并进行集合操作,那么使用 set
会更加合适。
2. map
和 set
的常见用法
2.1 如何创建和初始化 map
和 set
使用 map
和 set
时,可以通过简单的声明来创建它们。例如:
std::map<int, std::string> myMap;
std::set<int> mySet;
#include <set>
void TestSet()
{
// 用数组array中的元素构造set
int array[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0, 1, 3, 5, 7, 9, 2, 4,
6, 8, 0 };
set<int> s(array, array+sizeof(array)/sizeof(array));
cout << s.size() << endl;
// 正向打印set中的元素,从打印结果中可以看出:set可去重
for (auto& e : s)
cout << e << " ";
cout << endl;
// 使用迭代器逆向打印set中的元素
for (auto it = s.rbegin(); it != s.rend(); ++it)
cout << *it << " ";
cout << endl;
// set中值为3的元素出现了几次
cout << s.count(3) << endl;
}
set的模板参数列表:
set的构造 :
set的迭代器
set的容量
set修改操作
2.2 基本操作:插入、查找和删除
map
和 set
都提供了方便的接口进行插入、查找和删除操作。例如,在 map
中,可以使用 insert()
或者 []
来插入键值对,而在 set
中,可以使用 insert()
方法添加元素。查找操作通过 find()
函数来实现,删除操作则通过 erase()
完成。
myMap.insert({1, "one"});
myMap[2] = "two";
mySet.insert(3);
mySet.erase(2);
2.3 迭代器的使用:遍历 map
和 set
map
和 set
都支持迭代器来遍历容器中的元素。对于 map
,迭代器会返回键值对;对于 set
,则返回集合中的元素。
for (auto it = myMap.begin(); it != myMap.end(); ++it) {
std::cout << it->first << ": " << it->second << std::endl;
}
for (auto it = mySet.begin(); it != mySet.end(); ++it) {
std::cout << *it << std::endl;
}
2.4 常见的 map
与 set
的应用场景
map
常用于频繁的查找操作,例如字典查询、计数器等。set
常用于去重、集合运算、以及需要快速判断元素是否存在的场合。
以上就是关于map和set的介绍,下期我们会介绍他们的底层!