如何解决C ++将预先保留的哈希图std :: unordered_map与整数键和连续数据数组std :: vector进行比较
假设使用具有int
键类型的哈希映射结构:
std::unordered_map<int,data_type> um;
此外,当已知N
个元素的总数(或最大)时,可以预先构造哈希表。
um.reserve(N); // This will chainly call rehash() function...
据我所知,这里整数本身可以用作哈希表的 identity(hash)函数。
同时,对于连续数据集(即std::vector
或简单数组),可以通过从地址的位置进行移位随机访问最前面的数据。
两个容器都使用int
作为访问密钥,如下所示:
um[1] = data_type(1); //std::unordered_map<int,data_type>
v[1] = data_type(1); //std::vector<data_type>
然后,构造的哈希表和std::vector
在内存使用或搜索机制/性能或其他任何方面是否有区别?
让问题更明显。
如果我知道肯定使用了3个键0
,5
,9987
,但是可能会或可能不会使用键1
〜9986
。
如果我知道集合中的任何键都不会大于10000
,则使用大小为std::vector
的{{1}}将保证O(1)的时间复杂度,可访问随机数据,但可以存储将被浪费。
在这种情况下,10000
是否为该问题提供了更好的解决方案?
*我的意思是,这样一种解决方案可以节省尽可能多的内存,同时将时间复杂度保持在同一水平。
解决方法
一切都不同。
unordered_map的概念为buckets-
存储桶是容器内部哈希表中的插槽,根据其键的哈希值将元素分配给该插槽。存储桶的编号从0到(bucket_count-1)。
unordered_map计算指向存储桶的键的哈希值。所需值在该存储桶中。现在请注意,多个键可以指向一个存储桶。在您的情况下,甚至可能发生um[0]
,um[5]
和um[9987]
都位于同一存储桶中的情况!存储桶中的搜索时间是线性的。
在这种情况下,std :: unordered_map是否可以为该问题提供更好的解决方案?
如果数据稀疏,请使用unordered_map,但要保留适当的存储空间(或完全不保留存储空间,并使用默认分配策略)。如果执行myMap.reserve(MAX_ELEMENTS)
是没有意义的,因为那样会再次导致内存浪费。
否则,请使用向量。您将获得有保证的O(1)
查找。由于其线性,因此超级缓存友好。而在unordered_map上,您可能会得到最坏情况的查询O(N)
此外,当已知元素的总数(或最大)N时,可以预先构造哈希表。
um.reserve(N); //这将链接调用rehash()函数...
据我所知,这里整数本身可以用作哈希表的标识(哈希)函数。
这是正确的,并且在两种截然不同的情况下是合理的:1)当值非常连续且可能缺少一些值时,或者2)当值非常随机时。在许多其他情况下,如果您不提供有意义的哈希函数,则可能会导致哈希表冲突过多。
然后,构造的哈希表和std :: vector之间在内存使用率,搜索机制/性能或其他任何方面是否有所不同?
是的。在.reserve(N)
之后,哈希表会为至少N
个“存储桶”分配一个连续的内存块(基本上是一个数组)。如果考虑GCC实施,则N将四舍五入为一个素数。每个存储桶可以将迭代器存储到pair<int,data_type>
个节点的前向链接列表中。
因此,如果您实际上将N个条目放入哈希表中,则说明...
- 由> = N个元素组成的数组,大小为
sizeof(forward-list-iterator)
- N个内存分配为> =
sizeof(pair<int,data_type>) + sizeof(next-pointer/iterator for forward-list)
... vector
仅使用约N * sizeof(data_type)
字节的内存:可能是哈希表使用的一小部分内存,并且是data_type
的所有向量内存s是连续的,您很有可能会从与您当前尝试访问的元素相邻的CPU缓存元素中受益,从而使它们在以后访问时都快得多。
另一方面,如果您没有在哈希表中放入很多元素,则使用内存的主要功能是包含迭代器的存储桶阵列,这些存储桶通常是指针的大小(例如,每个指针的大小为32或64位) ,而data_type
的向量-如果您也有reserve(N)
-将已经分配了N * sizeof(data_type)
字节的内存-用于大型data_type
,它们可能比哈希大得多表。尽管如此,您仍然经常可以分配虚拟内存,并且如果您没有对内存页面进行故障处理而需要物理后备内存,则不会对程序或计算机造成有意义的内存使用或性能损失。 (至少对于64位程序,虚拟地址空间实际上是无限的。)
如果我知道肯定使用了3个按键0,5,9987,但是可能会或可能不会使用按键1〜9986。
如果我知道集合中没有键大于10000,那么使用大小为10000的std :: vector将保证O(1)访问随机数据的时间复杂度,但是会浪费内存。
在这种情况下,std :: unordered_map是否可以为该问题提供更好的解决方案? *我的意思是,这样一种解决方案可以节省尽可能多的内存,同时将时间复杂度保持在同一水平。
在这种情况下,如果您reversed(10000)
在前面,而data_type
并不比迭代器/指针大很多,那么unordered_map
的各个方面都会变得更加糟糕。如果您不预先预留,则哈希表将仅为少数几个存储桶分配空间,并且您使用的虚拟地址空间将比具有10000个元素的vector
少得多(即使{{1 }}是data_type
)。
如果仅要打包3个元素,则最佳解决方案是使用 Call createPK("dbo_tblHotels","ID")
:)比std::vector<std::pair<int,data_type>>
(实际上分配了多个向量桶)占用的内存更少,并且查找性能更好由于常量很小,对于少量元素也是最佳选择。
对于较大的地图,std::unordered_map<int,data_type>
和O(1)
都保证了std::vector<data_type>
的复杂性,但是向量对std::unordered_map<int,data_type>
的隐藏常数要低得多,因为它不会无需对照存储桶中的其他元素检查该元素。我建议始终使用向量,除非您没有足够的内存来适应它,在这种情况下,您可以使用O
通过牺牲一点性能来节省内存。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。