简介
底层实现
-
基于哈希表实现。
-
元素的存储顺序是无序的,取决于哈希函数和哈希表的结构。
-
插入、删除和查找操作的平均时间复杂度为O(1),但在最坏情况下(如大量哈希冲突)可能退化到O(n)。
查找性能
- 平均查找效率为O(1),适合频繁查找的场景,尤其是当数据量较大时,性能优势更明显。
插入和删除性能
- 平均情况下插入和删除操作的时间复杂度为O(1),但在大量哈希冲突时可能退化。
键的有序性
-
键是无序的,元素的存储和遍历顺序取决于哈希函数和哈希表的结构。
-
如果需要按键值顺序遍历,需要额外排序。
内粗使用
-
内存使用可能比
std::map稍高,因为哈希表需要额外的空间来处理哈希冲突(如开放寻址法或链表法)。 -
但可以通过调整负载因子(load factor)来优化内存使用。
使用场景
-
适用于需要频繁查找、插入和删除的场景,尤其是当数据量较大时。
哈希冲突和负载因子
-
哈希表的性能高度依赖于哈希函数的质量和负载因子。
-
负载因子(load factor)定义为
元素数量 / 哈希表大小。当负载因子过高时,哈希冲突会增加,性能会下降。 -
可以通过
rehash方法调整哈希表的大小,或者通过max_load_factor方法设置最大负载因子。
与map容器的比较
| 特性 | std::map | std::unordered_map |
|---|---|---|
| 底层实现 | 红黑树 | 哈希表 |
| 查找效率 | O(log n) | 平均O(1),最坏O(n) |
| 插入/删除效率 | O(log n) | 平均O(1),最坏O(n) |
| 键的有序性 | 有序 | 无序 |
| 内存使用 | 相对紧凑 | 可能稍高 |
| 使用场景 | 需要按键值有序存储 | 需要频繁查找、插入和删除 |
-
如果需要按键值有序存储或频繁进行范围查找,使用
std::map。 -
如果需要高效的查找、插入和删除操作,且对键值的顺序没有要求,使用
std::unordered_map。
成员函数
map和unordered_map虽然都是关联容器,用于存储键值对,但它们的内置函数并不完全相同。尽管它们有许多相似的成员函数,但由于底层实现和设计目标的不同,它们在某些函数上存在差异。
1. 共有的成员函数
map和unordered_map都继承自associative_container或unordered_associative_container,因此它们有许多共有的成员函数,例如:
-
begin()和end():返回迭代器,分别指向容器的开始和结束位置。 -
size()和empty():分别返回容器中元素的数量和判断容器是否为空。 -
clear():清空容器中的所有元素。 -
find():查找指定键的元素,返回一个迭代器。 -
count():统计指定键的元素数量(map中每个键只能有一个元素,unordered_map也是如此)。 -
insert():插入元素。 -
erase():删除元素。 -
operator[]和at():访问指定键的值。
上述函数的使用方法请参阅:C++ map容器总结
2.unordered_map独有的成员函数
-
load_factor()和max_load_factor():-
load_factor():返回当前的负载因子(元素数量除以哈希表的大小)。 -
max_load_factor():获取或设置最大负载因子。当负载因子超过最大值时,容器会自动重新哈希(rehash)。
-
unordered_map<int, string> myMap = {{1, "Apple"}, {2, "Banana"}, {3, "Cherry"}};
cout << "Current load factor: " << myMap.load_factor() << endl;
myMap.max_load_factor(0.75); // 设置最大负载因子
rehash():
-
手动重新哈希,调整哈希表的大小以适应当前的负载因子。
-
示例:
myMap.rehash(10); // 将哈希表大小调整为至少10
bucket_count() 和 bucket_size():
-
bucket_count():返回哈希表的桶(bucket)数量。 -
bucket_size(bucket_index):返回指定桶中的元素数量。 -
示例:
cout << "Number of buckets: " << myMap.bucket_count() << endl;
cout << "Size of bucket 0: " << myMap.bucket_size(0) << endl;
bucket(key):
-
返回指定键所在的桶的索引。
-
示例:
cout << "Bucket index for key 2: " << myMap.bucket(2) << endl;
插入和删除函数的差异
虽然std::map和std::unordered_map都有insert()、erase()和emplace()等函数,但它们的行为和效率有所不同:
-
insert():-
std::map:插入操作的时间复杂度为O(log n),因为它需要在红黑树中查找插入位置。 -
std::unordered_map:平均时间复杂度为O(1),但在最坏情况下(大量哈希冲突)可能退化到O(n)。
-
-
erase():-
std::map:删除操作的时间复杂度为O(log n),因为它需要在红黑树中查找并调整树结构。 -
std::unordered_map:平均时间复杂度为O(1),但在最坏情况下可能退化到O(n)。
-
-
emplace():-
std::map:直接在红黑树中构造元素,避免拷贝或移动,时间复杂度为O(log n)。 -
std::unordered_map:直接在哈希表中构造元素,平均时间复杂度为O(1),但在最坏情况下可能退化到O(n)。
-
迭代器行为的差异
-
std::map:-
迭代器是双向迭代器,支持双向遍历(
++和--)。 -
遍历顺序是按键值的升序(或自定义的比较顺序)。
-
-
std::unordered_map:-
迭代器是前向迭代器,仅支持单向遍历(
++)。 -
遍历顺序是无序的,取决于哈希表的结构。
-
2868

被折叠的 条评论
为什么被折叠?



