【问题标题】:Is finding a pair of equal integers in an array O(n)?在数组 O(n) 中找到一对相等的整数?
【发布时间】:2016-08-30 17:56:29
【问题描述】:

给定一个整数数组,找出相同整数对的最坏情况时间复杂度是多少?

我认为这可以通过使用计数排序或使用 XOR 在 O(n) 中完成。 我说的对吗?

问题不担心空间复杂度,答案是 O(nlgn)。

【问题讨论】:

  • 请找一个更有意义的标题。
  • @moooeeeep 世界并不像人们想象的那么完美,这很有趣
  • @KedarMhaswade 不,这是平均复杂度。由于冲突(可能是重新散列),哈希表的最坏情况搜索或插入是 O(n)。
  • @Willturner -- 检查表中是否已经存在整数,如果不存在则添加,如果存在则返回重复项。
  • 我认为这个问题没有明确说明。它应该说明是否有额外的内存可用。我相信在实践中,这可以使用哈希表在 O(n) 渐近时间内解决。您是否担心哈希函数?

标签: arrays algorithm sorting


【解决方案1】:

计数排序

如果输入允许您使用计数排序,那么您所要做的就是在 O(n) 时间内对输入数组进行排序,然后在 O(n) 时间内查找重复项。这个算法可以改进(虽然不复杂),因为您实际上不需要对数组进行排序。您可以创建计数排序使用的相同辅助数组,该数组由输入整数索引,然后将这些整数一一相加,直到已经插入当前整数。至此,已经找到了两个相等的整数。

此解决方案提供最坏情况、平均和最佳情况的线性时间复杂度 (O(n)),但要求输入整数在已知且理想的小范围内。

散列

如果您不能使用计数排序,那么您可以退回到散列并使用与以前相同的解决方案(不进行排序),使用散列表而不是辅助数组。哈希表的问题在于其操作的最坏情况时间复杂度是线性的,而不是恒定的。事实上,由于冲突和重新散列,在最坏的情况下,插入是在 O(n) 时间内完成的。

由于您需要 O(n) 次插入,这使得该解决方案的最坏情况时间复杂度为二次 (O(n²)),即使其平均和最佳情况时间复杂度是线性的(O(n))。

排序

如果计数排序不适用,另一种解决方案是使用另一种排序算法。基于比较的排序算法的最坏情况时间复杂度最多为 O(n log n)。解决方案是对输入数组进行排序并在 O(n) 时间内查找重复项。

此解决方案具有 O(n log n) 的最坏情况和平均时间复杂度,并且根据排序算法,最佳情况线性时间复杂度 (O(n) )。

【讨论】:

  • 这里没有给出整数大小或边界的条件,所以我可以说归并排序是最好的算法。 ?
  • 快速排序(比较排序)的最坏情况时间复杂度为 O(n^2):en.wikipedia.org/wiki/Quicksort#Worst-case_analysis
  • @KedarMhaswade 我认为您误读了(我编辑过)。我指出最坏情况的时间复杂度最多为 O(n log n)。当然,有些算法的时间复杂度为 O(n²) 或更差,但例如,smoothsort 的时间复杂度为 O(n)、O(n log n) 和 O(n log n)(最佳情况、平均和最差情况)案例,分别)。
  • @Willturner 有些算法比其他算法好,但你不能说归并排序是最好的。你可以看看this list;一些算法比归并排序具有更好的时间复杂度,但在实践中可能表现更差。快速排序、堆排序等也是如此。
  • 这个方法怎么样geeksforgeeks.org/…
【解决方案2】:

以下是Counting Sort的伪代码:

#    input -- the array of items to be sorted; key(x) returns the key for item x
#    n -- the length of the input
#    k -- a number such that all keys are in the range 0..k-1
#    count -- an array of numbers, with indexes 0..k-1, initially all zero
#    output -- an array of items, with indexes 0..n-1
#    x -- an individual input item, used within the algorithm
#    total, oldCount, i -- numbers used within the algorithm

# calculate the histogram of key frequencies:
for x in input:
    count[key(x)] += 1

# calculate the starting index for each key:
total = 0
for i in range(k):   # i = 0, 1, ... k-1
    oldCount = count[i]
    count[i] = total
    total += oldCount

# copy to output array, preserving order of inputs with equal keys:
for x in input:
    output[count[key(x)]] = x
    count[key(x)] += 1

return output

如您所见,所有键都在 0 ... k-1 的范围内。在您的情况下,数字本身是关键,它必须在一定范围内才能适用计数排序。只有这样才能在 O(n) 和 O(k) 空间内完成。

否则,使用任何基于比较的排序解决方案都是 O(nlogn)。

【讨论】:

    【解决方案3】:

    如果您订阅的整数排序是 O(n),那么通过排序 + 迭代直到两个相邻元素比较相等,这无论如何都是 O(n)。

    散列实际上是 O(n2) 在 worst 的情况下(你有世界上最差的散列算法,将所有内容散列到同一个索引)。尽管在实践中使用哈希表来获取计数会给您带来线性时间性能(平均情况)。

    实际上,线性时间整数通过将用于表示整数的位数固定为某个常数 k 来对“作弊”进行排序,以后可以忽略该常数。 (不过,在实践中,这些都是很好的假设,整数排序可以非常快!)

    基于比较的排序(如合并排序)在最坏的情况下会给您 O(n log n) 的复杂度。

    您所说的 XOR 解决方案是在两个其他相同的整数列表之间找到一个唯一的“额外”项。

    【讨论】:

    • 能否请您详细说明 XOR 行(最后一行)
    • 当然,一般来说你有两个相同的整数列表,除了其中一个有一个唯一元素。如果您从 0 开始并对两个列表中的每个元素进行异或运算,则除了唯一元素之外,它们都会相互抵消。
    • @AndyG 还有Cuckoo hashing 为插入提供预期的恒定时间。但它更难分析,并且可能还使用了一些技巧/作弊,正如您在基于计数的排序中所描述的那样。
    • @sascha:谢谢你,这是我的新作品。看起来 Cuckoo 散列是一种随机算法,它使用概率来获得良好的平均时间性能。当然,由于随机性,总是有可能出现最坏的情况,所以 Big-O 不会改变,但“坏”情况的可能性会变得非常小。
    • @AndyG 这当然是摊销分析。因此,加法的预期恒定时间操作已经在所有情况下(包括最坏情况)进行了平均。缓慢的最坏情况是可能的,但这意味着其他插入必须更快。
    猜你喜欢
    • 2018-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多