通常的哈希表实现与树相比

时间:2010-07-05 01:06:07

标签: c++ dictionary map hashtable

通常(如在C ++中),散列函数返回任何size_t值 - 因此可能有许多不同的散列值(2 ^ 32)。

这就是为什么我一直认为当人们谈到将它们作为表格实现时,实际上这并不是真的,因为表格会太大(2 ^ 32个条目)。当然,我的假设是错误的,表必须与整个哈希值范围一样大。

似乎实际执行比我想象的要困难。我一直想到的是,天真的实现是什么,如:

typedef list< pair<Key,Value> > Bucket;
typedef map< size_t, Bucket > Hashtable;

现在我的问题:这个天真的实现与复杂性(运行时和内存)方面的实际实现有何不同?

4 个答案:

答案 0 :(得分:3)

请注意,Matthieu M指出,还有其他方法可以实现哈希表。本答案的其余部分假设您希望对由某种列表组成的存储桶使用散列。

假设你在谈论时间复杂性。

哈希表应具有O(1)最佳案例访问权限。您在问题中实施的提议使用map<size_t,Bucket>来访问存储桶,这会导致O(log n)时间复杂度。您需要具有O(1)访问时间复杂度的内容,例如vector<Bucket>以符合哈希表的预期时间复杂度。

更多详情

哈希表可以在时间复杂度优异和差的时间之间变化,具体取决于它们的人口稀少程度。

在最好的情况下,每个桶最多只有一个条目,按键访问是O(1)。这是哈希表的常见引用复杂性。

在最坏的情况下,每个密钥具有相同的哈希值,并且按键访问有效地搜索导致O(n)行为的列表。

现实世界的使用通常介于这两个极端之间,希望更接近O(1)。

您的其他question接受的答案有一些简化的代码,您可以使用它们来解决这两个极端问题,以满足自己的需要。

答案 1 :(得分:0)

你的问题阿尔伯特的问题是,没有一个哈希表,其中有很多。

问题的核心在于某些操作的大O复杂性。平均而言,哈希表应该产生O(1)复杂度以找到项目。二叉树平均产生O(log N)。

就速度而言,它实际上取决于N的大小,因为它们是渐近的复杂性,因此当N很大(想想百万)时它们代表数量级,而小集合的实际速度可能大不相同。 / p>

因此,我认为您应该更好地掌握哈希表,而不是试图详细说明您的问题。快速概述:

  • 散列表可能会或可能不会根据桶实现:非桶实现包括开放寻址方案(顺便说一下,它对缓存更友好)。
  • 根据链表可以实现或不实现存储桶。其他方案包括使用另一个哈希函数(每个桶本身就是哈希表)或二叉树(map),后者需要一些排序。
  • 可以立即重新分配:即,一旦超出容量,就会分配一个新的(更大的)哈希表,并复制所有内容或使用线性重新分配方案来平滑重新分配成本并避免时间受到重大影响时间。

阅读维基百科上的文章,它解决了这些问题以及更多内容。

答案 2 :(得分:0)

这取决于。如果散列函数很好地实现了散列键的均匀分布并且表格不是太满,那么你将获得大约O(1)。哈希表将以相对较少的冲突获得正确的命中。

如果表格被广泛链接(即完整),则探测过程将花费更多时间来解决分裂。在理论上最坏的情况下,所有值都将映射到相同的散列键,并且散列函数将花费所有时间跟踪链,即O(n)。

在实践中,除非您的哈希函数真的被破坏,否则您应该为所有实际目的获得O(1)(请注意,您可以为较小的表获取较大哈希值的模数)。如果你有一个可以扩展的基于哈希表的容器,那么它可能会进行扩展操作,这将大大增加(1)。

树将是O(log n)而不是O(1),但如果树是不平衡的,那么搜索也可以变成有效的线性操作。请注意,在某些常见方案中这是一个问题,例如按键顺序插入节点时(想象一下基于树的集合上的浅复制操作)。通常,使用诸如红黑树的平衡树算法来保持树的有效性。树的另一个优点是可以按顺序遍历树并生成有序的键列表,而无需对它们进行显式排序。

  1. 有关扩展成本相对便宜的散列操作示例,请参阅Linear Hash Table(wi.ipedia.org)。

答案 3 :(得分:0)

实现可以轻松减少“超大”的密钥。例如,它可以使用以下结构:

typedef list< pair<Key,Value> > Bucket;
const int HashSize = 511;
Bucket[HashSize] Hashtable;
inline size_t HashIndex(Key k) { return hash(k) % HashSize; }

实际上,HashSize当然不是常数。如果插入超过几千个元素,这将导致性能急剧下降。此外,如果元素较少,它会占用相当多的内存。因此,实现使用该内部参数进行智能操作。结果,每个桶的值的数量是O(1),并且找到正确的桶也是O(1)。这就是这种实现可以在O(1)中检索任何值的方式。