【问题标题】:Why is a Hash Table with linked lists considered to have constant time complexity?为什么带有链表的哈希表被认为具有恒定的时间复杂度?
【发布时间】:2017-09-03 10:56:16
【问题描述】:

在昨晚的 COMP 课程中,我们了解了哈希以及在尝试在哈希表中查找元素 x 时它通常如何工作。

我们的场景是我们的表中有一个包含 1000 个元素的数据集,我们想知道 x 是否包含在该表中。

我们的教授绘制了一个包含 100 个元素的 Java 数组,并说要存储这 1000 个元素,数组的每个位置都将包含一个指向链接列表的指针,我们将在其中保存我们的元素。

假设散列函数将 1000 个元素中的每一个元素完美地映射到 0 到 99 之间的值,并将元素存储在数组中的位置,那么每个链表中将包含 1000/100 = 10 个元素。

现在要知道 x 是否在表中,我们只需对 x 进行哈希处理,找到它的哈希值,在该槽处查找数组并遍历我们的链表检查 x 是否在表中。

我的教授最后说,查找 x 是否在表中的预期复杂性是 O(10),实际上只是 O(1)。我无法理解这是怎么回事。在我看来,如果数据集是 N 并且数组大小是 n,那么平均需要 N/n 步才能在表中找到 x。从定义上看,这不是恒定时间吗,因为如果我们扩大数据集,时间仍然会增加?

我查看了 Stack Overflow 和在线,每个人都说散列是 O(1) 的预期时间复杂度,但有一些警告。我读过人们讨论链接以减少这些警告。也许我错过了一些关于确定时间复杂度的基本知识。

TLDR:为什么在哈希表中查找一个值需要 O(1) 时间,而它似乎仍然取决于数据集的大小(因此是 N 的函数,因此不是常数)。

【问题讨论】:

标签: java hash time-complexity hashtable


【解决方案1】:

在我看来,如果数据集为 N,数组大小为 n,那么平均需要 N/n 步才能在表中找到 x。

这是一种误解,因为散列只需要您计算应存储对象的正确存储桶(在本例中为数组索引)。如果数据集的大小发生变化,此计算不会变得更复杂.

您所说的这些警告很可能是哈希冲突:多个对象共享相同的 hashCode;这些可以通过更好的哈希函数来防止。

【讨论】:

  • 我认为 OP 的困惑不在于评估哈希函数的复杂性,也不在于它如何分配数据。而是如果我们总是使用,比如说,100 个桶,那么随着数据集变得越来越大,我们将不可避免地在每个桶中存储越来越多的项目,并且在 inside 中找到项目所需的时间桶会增加。当然,这里的关键是我们可以选择更多的桶来容纳更大的数据集,而不会让每个桶中的列表变得太长。
  • @CAW 理论上是的,但是我相信 OP 将他的 LinkedLists 限制为 10。在这种情况下,在已经满的 Hashtable 中找到对象所需的时间即使数据集的大小继续增加,也将保持不变。但我同意增加存储桶的数量也是一个有效的解决方案。
  • 我认为 OP 只考虑限制存储桶的数量(即数组的大小),而不是列表的大小:“如果数组大小为 n,则平均需要 N/在表格中找到 x 的 n 步”(意味着每个列表平均包含 N/n 个项目,或在他们的示例中为 1000/100=10)
  • @CAW 如果他仍然感到困惑,那么 OP 可以为我们提供一些澄清。
  • @CAW 是的,当然,如果你有一个更大的数组,那么你就可以避免冲突。但是,在我提供的示例中存在冲突,所以我不明白这仍然是恒定的时间。
【解决方案2】:

查找的散列集合的复杂度为 O(1),因为每个桶的列表(或在 Java 的情况下为红黑树)的大小不依赖于 N。HashMap 的最坏情况性能如果你有一个非常糟糕的散列函数是 O(log N),但正如 Javadocs 指出的那样,你会得到 O(1) 的性能“假设散列函数将元素正确地分散在桶中”。通过适当的分散,每个桶的集合的大小或多或少是固定的,并且足够小,以至于常数因子通常会压倒多项式因子。

【讨论】:

    【解决方案3】:

    这里有多个问题,所以我将一一解决:

    最坏情况分析与摊销分析:

    最坏情况分析是指您的算法可以相对于运行时间给出的绝对最坏情况。举个例子,如果我给出一个无序元素数组,并且我被告知要在其中找到一个元素,我最好的情况是当元素位于索引 [0] 时,我可以给出的最糟糕的情况是元素位于数组的末尾,在这种情况下,如果我的数据集是 n,我会在找到元素之前运行 n 次。但是,在平均情况下,该元素位于数组中的任何位置,因此我将运行 n-k 步(其中 k 是我在数组中查找的元素之后的元素数)。

    Hashtables的最坏情况分析: 只有一种 Hashtable 保证了对它的元素数组的恒定时间访问 O(1)。 (即使那样,分页和操作系统处理内存的方式实际上也不是真的)。对于哈希表,我可以为您提供的最糟糕的情况是每个元素都散列到同一个索引的数据集。因此,例如,如果每个元素都哈希到索引 1,由于冲突,访问值的最坏情况运行时间是 O(n)。这是不可避免的,哈希表总是有这种行为。

    哈希表的平均和最佳情况: 你很少会得到一个给你最坏情况的集合。通常,您可以期望对象被散列到散列表中的不同索引。理想情况下,散列函数以非常分散的方式散列事物,以便对象被散列到散列表中的不同索引。

    在你的老师给你的具体例子中,如果两件事被哈希到同一个索引中,它们就会被放入一个链表中。所以这或多或少是表格的构造方式:

    get element E
    use the hashing function hash(E) to find the index i in the hash table
    add e to the linjed list in hashTable[i].
    
    repeat for all the elements in the data set
    

    所以现在,假设我要查找元素 E 是否在桌子上。然后:

    do hash(E) to find the index i where E is potentially hashed
    
    go to hashTable[i] and iterate through the linked list (up to 10 iterations)
    
    If E is found, then E is in the Hash table, if E is not found, then E is not in the table
    

    如果我们找不到它,我们可以保证 E 不在表中的原因是因为如果它是,它会被散列到 hashTable[i] 所以它必须在那里,如果它在桌子。

    【讨论】:

    • 实际上对于 Java 的HashMap,理论上最坏的检索情况是 O(log N)。
    猜你喜欢
    • 2011-08-18
    • 1970-01-01
    • 2020-09-11
    • 2019-02-19
    • 2014-12-20
    • 1970-01-01
    • 2011-04-26
    • 1970-01-01
    • 2020-11-19
    相关资源
    最近更新 更多