C++ 常用哈希表实现与树的比较

C++ 常用哈希表实现与树的比较,c++,dictionary,map,hashtable,C++,Dictionary,Map,Hashtable,通常(在C++中),哈希函数返回任意大小的值——因此可能有许多不同的哈希值(2^32) 这就是为什么我总是认为,当人们谈论将它们实现为表时,实际上并不是这样,因为表太大了(2^32个条目)。当然,我的假设是错误的,即表必须与整个散列值范围一样大 实际执行起来似乎比我想象的要困难。我一直在想的是,什么是天真的实现,是这样的: typedef list< pair<Key,Value> > Bucket; typedef map< size_t, Bucket >

通常(在C++中),哈希函数返回任意大小的值——因此可能有许多不同的哈希值(2^32)

这就是为什么我总是认为,当人们谈论将它们实现为表时,实际上并不是这样,因为表太大了(2^32个条目)。当然,我的假设是错误的,即表必须与整个散列值范围一样大

实际执行起来似乎比我想象的要困难。我一直在想的是,什么是天真的实现,是这样的:

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

现在我的问题是:在复杂性(运行时和内存)方面,这种幼稚的实现与实践中的实际实现有何不同?

请注意,正如Matthieu M所指出的,还有其他实现哈希表的方法。这个答案的其余部分假设您希望对由某种列表组成的桶使用哈希

假设你说的是时间复杂性

哈希表应具有O(1)最佳情况访问。您在问题中的实施方案使用了一个用于访问bucket的方法,这将导致O(logn)时间复杂性。您需要具有O(1)访问时间复杂度的内容,例如a,以符合哈希表的预期时间复杂度

更多细节 哈希表的时间复杂度可以是好的,也可以是差的,这取决于它们的填充密度

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

在最坏的情况下,每个键都有相同的散列值,按键访问有效地搜索一个列表,从而导致O(n)行为

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


对于你的另一个答案,你可以使用一些简化的代码来处理这两个极端,以满足自己的需求。

注意,正如Matthieu M所指出的,还有其他实现哈希表的方法。这个答案的其余部分假设您希望对由某种列表组成的桶使用哈希

假设你说的是时间复杂性

哈希表应具有O(1)最佳情况访问。您在问题中的实施方案使用了一个用于访问bucket的方法,这将导致O(logn)时间复杂性。您需要具有O(1)访问时间复杂度的内容,例如a,以符合哈希表的预期时间复杂度

更多细节 哈希表的时间复杂度可以是好的,也可以是差的,这取决于它们的填充密度

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

在最坏的情况下,每个键都有相同的散列值,按键访问有效地搜索一个列表,从而导致O(n)行为

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


你的另一个问题的公认答案有一些简化的代码,你可以使用这些代码来处理这两个极端,以满足你自己的需求。

你的问题的关键是,Albert,没有一个哈希表,有很多哈希表

这里问题的核心是某些操作的big-O复杂性。平均而言,哈希表应该产生O(1)复杂度来查找项。二叉树平均产生O(logn)

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

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

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

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

阿尔伯特,你的问题的关键是没有一个哈希表,而是有很多

这里问题的核心是某些操作的big-O复杂性。平均而言,哈希表应该产生O(1)复杂度来查找项。二叉树平均产生O(logn)

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

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

  • 哈希表可以按照bucket实现,也可以不按照bucket实现:非bucket实现包括开放寻址方案(顺便说一句,开放寻址方案对缓存更友好)
  • bucket可以通过链表实现,也可以不通过链表实现。其他方案包括使用另一个哈希函数(每个bucket本身就是一个哈希表)或二叉树(map),后者需要一些排序
  • 重新分配可以立即完成:即一旦超过容量,就会分配一个新的(更大的)哈希表,并生成一个
    typedef list< pair<Key,Value> > Bucket;
    const int HashSize = 511;
    Bucket[HashSize] Hashtable;
    inline size_t HashIndex(Key k) { return hash(k) % HashSize; }