C++ unordered_map 容器总结

简介

底层实现

  • 基于哈希表实现。

  • 元素的存储顺序是无序的,取决于哈希函数和哈希表的结构。

  • 插入、删除和查找操作的平均时间复杂度为O(1),但在最坏情况下(如大量哈希冲突)可能退化到O(n)。

查找性能

  • 平均查找效率为O(1),适合频繁查找的场景,尤其是当数据量较大时,性能优势更明显。

插入和删除性能

  • 平均情况下插入和删除操作的时间复杂度为O(1),但在大量哈希冲突时可能退化。

键的有序性

  • 键是无序的,元素的存储和遍历顺序取决于哈希函数和哈希表的结构。

  • 如果需要按键值顺序遍历,需要额外排序。

内粗使用

  • 内存使用可能比std::map稍高,因为哈希表需要额外的空间来处理哈希冲突(如开放寻址法或链表法)。

  • 但可以通过调整负载因子(load factor)来优化内存使用。

使用场景

  • 适用于需要频繁查找、插入和删除的场景,尤其是当数据量较大时。

哈希冲突和负载因子

  • 哈希表的性能高度依赖于哈希函数的质量和负载因子。

  • 负载因子(load factor)定义为元素数量 / 哈希表大小。当负载因子过高时,哈希冲突会增加,性能会下降。

  • 可以通过rehash方法调整哈希表的大小,或者通过max_load_factor方法设置最大负载因子。

与map容器的比较

特性std::mapstd::unordered_map
底层实现红黑树哈希表
查找效率O(log n)平均O(1),最坏O(n)
插入/删除效率O(log n)平均O(1),最坏O(n)
键的有序性有序无序
内存使用相对紧凑可能稍高
使用场景需要按键值有序存储需要频繁查找、插入和删除
  • 如果需要按键值有序存储或频繁进行范围查找,使用std::map

  • 如果需要高效的查找、插入和删除操作,且对键值的顺序没有要求,使用std::unordered_map

成员函数

mapunordered_map虽然都是关联容器,用于存储键值对,但它们的内置函数并不完全相同。尽管它们有许多相似的成员函数,但由于底层实现和设计目标的不同,它们在某些函数上存在差异。

1. 共有的成员函数

mapunordered_map都继承自associative_containerunordered_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::mapstd::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

    • 迭代器是前向迭代器,仅支持单向遍历(++)。

    • 遍历顺序是无序的,取决于哈希表的结构。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值