Algorithm 哈希表真的是O(1)吗?

Algorithm 哈希表真的是O(1)吗?,algorithm,performance,language-agnostic,big-o,hashtable,Algorithm,Performance,Language Agnostic,Big O,Hashtable,散列表可以实现O(1),这似乎是常识,但这对我来说从来没有意义。有人能解释一下吗?以下是我想到的两种情况: A.该值是一个小于哈希表大小的整数。因此,该值是它自己的哈希,因此没有哈希表。但如果有,它将是O(1),并且仍然是低效的 B.您必须计算该值的散列。在这种情况下,所查找数据的大小顺序为O(n)。在你做了O(n)个工作之后,查找可能是O(1),但在我看来仍然是O(n) 除非您有一个完美的散列或一个大的散列表,否则每个bucket可能有几个项。所以,无论如何,它在某一点上退化为一个小的线性搜索

散列表可以实现O(1),这似乎是常识,但这对我来说从来没有意义。有人能解释一下吗?以下是我想到的两种情况:

A.该值是一个小于哈希表大小的整数。因此,该值是它自己的哈希,因此没有哈希表。但如果有,它将是O(1),并且仍然是低效的

B.您必须计算该值的散列。在这种情况下,所查找数据的大小顺序为O(n)。在你做了O(n)个工作之后,查找可能是O(1),但在我看来仍然是O(n)

除非您有一个完美的散列或一个大的散列表,否则每个bucket可能有几个项。所以,无论如何,它在某一点上退化为一个小的线性搜索

我认为哈希表非常棒,但是我没有得到O(1)的名称,除非它只是理论上的

Wikipedia一贯引用恒定的查找时间,完全忽略了哈希函数的开销。这真的是一个公平的衡量标准吗


编辑:总结我学到的知识:

  • 这在技术上是正确的,因为哈希函数不需要使用密钥中的所有信息,因此可以是常数时间,并且因为足够大的表可以将冲突降低到接近常数时间

  • 这在实践中是正确的,因为随着时间的推移,只要选择哈希函数和表大小以最小化冲突,它就会起作用,即使这通常意味着不使用固定时间的哈希函数


哈希是固定大小的-查找适当的哈希桶是一个固定成本的操作。这意味着它是O(1)


计算散列不一定是一个特别昂贵的操作-我们这里不讨论加密散列函数。但这是顺便提一下。散列函数计算本身不依赖于元素数n;虽然它可能取决于元素中数据的大小,但这不是n所指的。因此散列的计算不依赖于n,也是O(1)。

这里有两个变量,m和n,其中m是输入的长度,n是散列中的项数

O(1)查找性能声明至少做出两个假设:

  • 您的对象可以在O(1)时间内进行相等比较
  • 将有一些散列冲突
如果您的对象大小可变,并且相等检查需要查看所有位,那么性能将变为O(m)。但是,哈希函数不必是O(m)-它可以是O(1)。与加密散列不同,字典中使用的散列函数不必为了计算散列而查看输入中的每一位。实现只需查看固定数量的位即可

对于足够多的项,项的数量将大于可能的散列数,然后您将得到冲突,导致性能上升到O(1)以上,例如,对于简单的链表遍历为O(n)(或者,如果两个假设都为false,则为O(n*m))

在实践中,尽管O(1)索赔在技术上是错误的,但在许多真实情况下,尤其是在上述假设成立的情况下,O(1)索赔几乎是正确的

您必须计算散列,因此要查找的数据大小的顺序是O(n)。在你做了O(n)个工作之后,查找可能是O(1),但在我看来仍然是O(n)

什么?散列单个元素需要固定的时间。为什么会是别的呢?如果要插入
n
元素,则必须计算
n
散列,这需要线性时间。。。要查找元素,您需要计算所查找内容的单个散列,然后使用该散列找到相应的bucket。您不需要重新计算哈希表中已经存在的所有内容的哈希

除非你有一个完美的散列或者一个大的散列表,否则每个bucket可能有几个条目,所以不管怎样,它在某个时候都会转化为一个小的线性搜索

不一定。bucket不一定必须是列表或数组,它们可以是任何容器类型,例如平衡的BST。这意味着
O(logn)
最坏的情况。但这就是为什么选择一个好的散列函数来避免在一个bucket中放入太多元素是很重要的。正如肯尼特指出的那样,平均而言,你仍然会得到
O(1)
时间,即使偶尔你不得不挖桶

当然,哈希表的权衡是空间复杂性。你在用空间换取时间,这似乎是计算科学中的常见情况


您提到在其他注释中使用字符串作为键。您关心计算字符串哈希所需的时间,因为它由几个字符组成?正如其他人再次指出的那样,您不一定需要查看所有字符来计算散列,尽管如果这样做,可能会产生更好的散列。在这种情况下,如果您的密钥中平均有
m
个字符,并且您使用了所有这些字符来计算哈希,那么我认为您是对的,查找将花费
O(m)
。如果
m>>n
则您可能有问题。如果是那样的话,你最好有个BST。或者选择一个更便宜的散列函数。

只有在表中只有恒定数量的键并且做出一些其他假设的情况下,散列才是O(1)。但在这种情况下,它有优势

如果密钥具有n位表示,则哈希函数可以使用1、2、。。。这些比特中的n个。考虑使用1位的哈希函数。评估肯定是O(1)。但您只是将密钥空间划分为2。因此,您要将多达2^(n-1)个键映射到同一个bin中。使用BST搜索,如果某个密钥几乎已满,则最多需要n-1个步骤来定位该密钥

你可以把它拔出来
function get(a: Table with m buckets, k: Key being looked up)
  bucket <- compute hash(k) modulo m
  for each (key,value) in a[bucket]
    return value if k == key
  return not_found
function get(a: Table with m buckets and seed r, k: Key being looked up)
  rHash <- H[r]
  bucket <- compute rHash(k) modulo m
  for each (key,value) in a[bucket]
    return value if k == key
  return not_found