C:在一个哈希表中存储多达一百万个条目

C:在一个哈希表中存储多达一百万个条目,c,optimization,performance,hashmap,hashtable,C,Optimization,Performance,Hashmap,Hashtable,我正在从事一个效率至关重要的项目。哈希表将非常有用,因为我需要根据密钥轻松查找节点的内存地址。我预见到的唯一问题是这个哈希表将需要处理多达100万个条目。据我所知,散列表bucket通常是一个链表,因此它们可以处理同一bucket中的多个条目。在我看来,有一百万个条目,这些列表的速度太慢了。实现这样的东西的常见方式是什么。是否可以将标准链表替换为跳过列表?您可以使用树而不是单个“bucket”中的列表。(AVL或类似产品) 编辑:嗯,跳过列表也可以。(而且似乎更快)-O(logn)是您的目标。如

我正在从事一个效率至关重要的项目。哈希表将非常有用,因为我需要根据密钥轻松查找节点的内存地址。我预见到的唯一问题是这个哈希表将需要处理多达100万个条目。据我所知,散列表bucket通常是一个链表,因此它们可以处理同一bucket中的多个条目。在我看来,有一百万个条目,这些列表的速度太慢了。实现这样的东西的常见方式是什么。是否可以将标准链表替换为跳过列表?

您可以使用树而不是单个“bucket”中的列表。(AVL或类似产品)


编辑:嗯,跳过列表也可以。(而且似乎更快)-O(logn)是您的目标。

如果每个存储桶列表包含的条目超过几个,您将失去哈希表的所有优势。使哈希表扩展到数百万个条目的常用方法是使主哈希数组的大小可调整,因此即使有数百万个条目,存储桶列表也会很短。

不确定这是否会对您有所帮助,但可能:

如果您想要一个包含一百万个条目的哈希表,通常您至少会有200万个存储桶。我不记得所有的统计数据(关键术语是“生日悖论”),但绝大多数水桶都没有或只有一个项目。原则上,你可以非常不走运,把所有的东西都放在一个桶里——但你必须比那些似乎每隔一天就被闪电击中的人更不走运

对于增长的哈希表,正常的技巧是以恒定的百分比增长——通常教科书中的情况是通过将哈希表大小加倍来增长。每当哈希表中的项数达到哈希表大小的某个比例时,不管实际使用了多少个bucket,都会执行此操作。这使得插入、删除和搜索的摊销预期性能为O(1)

哈希表的每个bucket中的链表只是一种处理冲突的方法——从每个操作的意义上讲,这是不可能的,但在一个重要哈希表的生命周期中,它们确实会发生——特别是当哈希表超过一半时

链表不是处理冲突的唯一方法——关于这个主题有大量的知识。Walter Bright(D编程语言的开发人员)提倡使用二叉树而不是链表,声称他的Dscript通过这种设计选择获得了相对于Javascript的显著性能提升

当我询问时,他使用了简单(不平衡)二叉树,因此最坏情况下的性能与链表相同,但我想关键点是二叉树处理代码很简单,而哈希表本身使得构建大型不平衡树的几率非常小


原则上,您可以同样轻松地使用treaps、红黑树或AVL树。一个有趣的选择可能是使用八字树进行碰撞处理。但是总的来说,对于一些库设计者和一些真正的痴迷者来说,这是一个需要担心的小问题。

条目的总数并不重要,只是每个bucket的平均条目数(N/散列大小)。使用具有更大域(例如,20位或更大)的哈希函数来确保


当然,这会占用更多内存,但就是这样,这是一个常见的内存与速度的折衷。

如果您的密钥具有正态分布(这是一个非常大的If),那么在哈希表中插入以耗尽哈希表中所有存储桶的预期次数是M*logM(自然对数,以e为底),其中M是存储桶的数量

很惊讶在网上找不到这个


我已经在上发布了相同的推导,并使用rand()用代码验证了它。这似乎是一个相当好的估计。

我不得不想象调整哈希表的大小是一个相当昂贵的操作。有什么比遍历每个条目更好的方法吗?@blcArmadillo:您不必调整它的大小,它的大小应该在创建时决定。@blcArmadillo-从摊销的意义上讲,如果您将表的大小调整一倍,这是非常便宜的。调整表的大小是O(n),但很少这样做。基本上,对于每一次插入,你都要“收取”一个额外的单位积分来支付调整大小的费用——每个操作只收取固定金额的费用,而调整大小(发生时)是从“节省的时间”中支付的。@ruslik——这在现实世界中并不实用。您通常不知道在开始之前需要插入多少项。据我所知,标准库中的所有主要哈希表实现(Python、Java、C#、C++0x、Javascript等)都会根据插入的项数增长/收缩哈希表。@Steve314:你说得对,这很有意义,特别是对于一般库。关于该图像的一些信息。。。严格地说,哈希表已经给出了预期的O(1)。如果您正在寻找一个渐进的改进,那么您需要一个用于冲突处理的有保证的平衡树——具有最坏情况O(logn)的树,以便为哈希表本身提供摊销的最坏情况O(logn)操作,而不是摊销的最坏情况O(n)。因为跳过列表是随机化的,所以它不会提供这种保证-尽管它在实践中可能会提高性能。O(logn)用于冲突处理的一个存储桶中的结构。还有另一种处理冲突的方法是布谷鸟哈希法。该理论似乎排除了一对密钥在两个哈希中发生冲突的可能性,或者一个密钥的两个可能位置由于冲突而不可用。可能意味着我还没有充分调查。我以前见过双哈希方案,但在五分钟前阅读您的评论之前没有听说过术语“布谷鸟哈希”,因此我很容易错过一两个特定的技巧。@Steve314这很容易递归处理(在