通常(如在C ++中),散列函数返回任何size_t值 - 因此可能有许多不同的散列值(2 ^ 32)。
这就是为什么我一直认为当人们谈到将它们作为表格实现时,实际上这并不是真的,因为表格会太大(2 ^ 32个条目)。当然,我的假设是错误的,表必须与整个哈希值范围一样大。
似乎实际执行比我想象的要困难。我一直想到的是,天真的实现是什么,如:
typedef list< pair<Key,Value> > Bucket;
typedef map< size_t, Bucket > Hashtable;
现在我的问题:这个天真的实现与复杂性(运行时和内存)方面的实际实现有何不同?
答案 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>
因此,我认为您应该更好地掌握哈希表,而不是试图详细说明您的问题。快速概述:
阅读维基百科上的文章,它解决了这些问题以及更多内容。
答案 2 :(得分:0)
这取决于。如果散列函数很好地实现了散列键的均匀分布并且表格不是太满,那么你将获得大约O(1)。哈希表将以相对较少的冲突获得正确的命中。
如果表格被广泛链接(即完整),则探测过程将花费更多时间来解决分裂。在理论上最坏的情况下,所有值都将映射到相同的散列键,并且散列函数将花费所有时间跟踪链,即O(n)。
在实践中,除非您的哈希函数真的被破坏,否则您应该为所有实际目的获得O(1)(请注意,您可以为较小的表获取较大哈希值的模数)。如果你有一个可以扩展的基于哈希表的容器,那么它可能会进行扩展操作,这将大大增加(1)。
树将是O(log n)而不是O(1),但如果树是不平衡的,那么搜索也可以变成有效的线性操作。请注意,在某些常见方案中这是一个问题,例如按键顺序插入节点时(想象一下基于树的集合上的浅复制操作)。通常,使用诸如红黑树的平衡树算法来保持树的有效性。树的另一个优点是可以按顺序遍历树并生成有序的键列表,而无需对它们进行显式排序。
答案 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)中检索任何值的方式。