缺少数字 面试问题 Redux
Posted
技术标签:
【中文标题】缺少数字 面试问题 Redux【英文标题】:Missing number(s) Interview Question Redux 【发布时间】:2011-05-23 07:26:09 【问题描述】:确定从 1 到 N 范围内的缺失值的常见面试问题已经完成了一千次。变体包括 2 个缺失值,最多 K 个缺失值。
示例问题:范围 [1,10] (1 2 4 5 7 8 9 10) = 3,6
以下是各种解决方案的示例:
Easy interview question got harder: given numbers 1..100, find the missing number(s)
我的问题是,将一个缺失值的简单情况视为 O(n) 复杂度,而较大情况的复杂度大致收敛于大于 O(nlogn) 的值:
通过对范围进行排序 (mergesort) 并遍历它观察丢失的元素,难道不是更容易回答问题吗?
此解决方案应花费不超过 O(nlogn) 并且能够解决 1 到 N 以外的范围的问题,例如 10 到 1000 或 -100 到 +100等等……
是否有任何理由相信上述 SO 链接中的给定解决方案会比基于排序的解决方案更好地处理大量缺失值?
注意:这个问题似乎有很多常见的解决方案,假设是唯一的数论方法。如果有人在 S/E 面试中被问到这样的问题,假设该方法与数论解决方案的复杂性相当,那么使用更计算机科学/算法的方法不是明智之举……
更多相关链接:
https://mathoverflow.net/questions/25374/duplicate-detection-problem How to tell if an array is a permutation in O(n)?【问题讨论】:
排序或使用 BitSet 是完全有效的解决方案,除非面试官明确指出他正在寻找流式算法或该集合需要太多内存。 如果您甚至没有 O(N) 可用内存怎么办?如果您必须在资源非常有限的嵌入式设备上实现这一点,并且输入以流的形式出现,没有随机访问,该怎么办? 这个答案的问题是 OP 在特别需要 O(K) 空间的问题中发布了这个;而这个答案需要 O(N) 空间。 OP将其他答案(其中一些非常好)描述为“荒谬的答案”。 问题是最有意义的,当输入以流的方式给出时:您实际上无法将所有n
项目存储在内存中,但它们一次显示给您一个。你只有k^O(1)
内存可以玩。在这种情况下,幂和技术是有意义的。您还可以通过 ***.com/a/36851791/205521 中的散列来改进“显示的每个数字使用的时间”
@javadba,原来的问题确实要求O(k)
空间复杂度,但是堆排序可以解决O(1)
空间中的问题。
【参考方案1】:
您只是指定时间复杂度,但空间复杂度也很重要。
问题复杂度可以用N
(范围的长度)和K
(缺失元素的数量)来指定。
在您链接的问题中,使用方程的解决方案是空间中的 O(K)(或者可能更多?),因为每个未知值都需要一个方程。
还有一个保留点:您可以更改已知元素的列表吗?在许多情况下,这是不可取的,在这种情况下,任何涉及对元素重新排序或使用它们的解决方案都必须首先在空间中进行 O(N-K) 复制。
我无法比线性解决方案更快:您需要读取所有已知元素 (N-K) 并输出所有未知元素 (K)。因此,您无法及时获得比 O(N) 更好的结果。
让我们分解解决方案
销毁,O(N) 空间,O(N log N) 时间:就地排序 保留,O(K) 空间?,O(N log N) 时间:方程系统 保留,O(N) 空间,O(N) 时间:计数排序就个人而言,虽然我发现 方程系统 解决方案很聪明,但我可能会使用任何一种排序解决方案。让我们面对现实吧:它们的编码要简单得多,尤其是计数排序!
就时间而言,在实际执行中,我认为“计数排序”将击败所有其他解决方案。
注意:计数排序不需要范围是[0, X)
,任何范围都可以,因为任何有限范围都可以通过简单的翻译转换为[0, X)
形式。
编辑:
将排序更改为 O(N),需要有所有可用的元素来对它们进行排序。
在考虑了这个问题之后,我还提出了另一个解决方案。如前所述,当 N (急剧)增长时,所需的空间可能会爆炸。但是,如果 K 很小,那么我们可以使用间隔来更改列表的表示:
4, 5, 3, 1, 7
可以表示为
[1,1] U [3,5] U [7,7]
在一般情况下,维护一个排序的区间列表比维护一个排序的元素列表的成本要低得多,而且推断缺失的数字也很容易。
时间复杂度很简单:O(N log N),毕竟它基本上是一个插入排序。
当然,真正有趣的是不需要实际存储列表,因此您可以将其与流一起提供给算法。
另一方面,我很难计算出平均空间复杂度。 “最终”占用的空间为 O(K)(最多为 K+1 个区间),但在构建过程中,由于我们以不特定的顺序引入元素,因此会丢失更多的区间。
最坏的情况很简单:N/2 个区间(想想奇数和偶数)。但是,我无法弄清楚平均情况。我的直觉告诉我它应该比 O(N) 更好,但我没有那么信任。
【讨论】:
空间复杂度无关紧要,因为问题的措辞可能比可用 RAM 大得多,或者输入应该被视为在线问题 - 真的不知道有多少直到遇到最后一个元素,这意味着需要面向磁盘/在线排序技术,因此销毁似乎是可行的... @Oxsnarder:恐怕我听不懂你。空间复杂度总是相关的。如果您使用面向磁盘或在线分析技术,那么您正在使用额外的空间,这需要考虑在内。事实上,输入越大,您就越需要在时间和空间复杂性之间取得平衡,因为磁盘访问次数要多得多比 RAM 访问昂贵。我将修改排序的空间复杂度,因为你需要一次保存所有元素,所以你需要 O(N) 空间。 为什么你认为 O(n) 空间用于就地排序算法?例如,堆排序使用 O(1) 空间。 @eversor:我在考虑输入的大小……不管你做什么,你的输入都有N个元素。 @MatthieuM.,我喜欢你在这里的见解,但是 10 年过去了,现在知道这个问题可以在O(n)
时间和O(k)
空间中简单地解决(如果你被允许编辑输入数组)。【参考方案2】:
给定解决方案在理论上是否优于排序解决方案取决于 N 和 K。虽然您的解决方案具有 O(N*log(N))
的复杂性,但给定解决方案是 O(N*K)
。我认为给定的解决方案(与排序解决方案相同)能够通过将范围[A, B]
转换为[1, N]
来解决任何范围[A, B]
。
【讨论】:
【参考方案3】:这个怎么样?
-
创建您自己的包含所有数字的集合
从您的集合中删除给定的一组数字(无需排序)
你的集合中剩下的是缺失的数字。
【讨论】:
可惜这有O(n)
的空间复杂度和充其量O(n)
的时间复杂度,所以我们还不如直接用sort(计数排序)来解决这个问题。【参考方案4】:
我的问题是,看到 [...] 案例大致收敛于 大于 O(nlogn) [...]
2011 年(在您发布此问题后)Caf
发布了a simple answer,解决了O(n)
时间和O(k)
空间[其中数组大小为n - k
] 的问题。
重要的是,与其他解决方案不同,Caf 的答案没有隐藏的内存要求(使用位数组、向元素添加数字、将元素乘以 -1
- 这些都需要 O(log(n))
空间)。
注意:此处的问题(以及原始问题)并未询问有关问题的流式传输版本,此处的答案也不处理这种情况。
关于其他答案:我同意这个问题的许多提议的“解决方案”都有可疑的复杂性声明,如果它们的时间复杂度在某些方面并不比任何一个更好:
计数排序(O(n)
时空)
比较(堆)排序(O(n*log(n))
时间,O(1)
空间)
...那么你也可以通过排序来解决问题。
但是,我们可以获得更好的复杂性(更重要的是,真正更快的解决方案):
【讨论】:
【参考方案5】:由于这些数字取自一个很小的有限范围,因此可以在线性时间内对它们进行“排序”。
我们所做的只是初始化一个包含 100 个布尔值的数组,并为每个输入设置与输入中每个数字对应的布尔值,然后逐步报告未设置的布尔值。
【讨论】:
我的建议不需要额外的空间超过缺失元素的数量,也不需要范围在1-N之间,只需对范围进行排序和迭代,添加缺失的列表中的元素。我相信其他 SO 帖子中提供的解决方案不仅仅是 O(n) @Oxsnarder:对其他变量没有限制。此外,此方法比通用解决方案更快(并且问题正是出于这个原因明确指定了范围)。当然,您应该执行分析以查看哪种方法更适合特定情况,但对于小范围和缺失元素的数量,计数排序是赢家。 我认为这不能回答问题?【参考方案6】:如果总共有 N 个元素,其中每个数字 x 满足 1 那么我们可以解决这个问题O(nlogn) 时间复杂度和 O(1) 空间复杂度。
-
首先使用快速排序或归并排序对数组进行排序。
扫描已排序的数组,如果先前扫描的数字 a 与当前数字 b 之间的差值等于 2 (b - a = 2),则缺少的数字是 a+1。这可以扩展到 (b - a > 2) 的条件。
当N > 100时,时间复杂度为O(nlogn)+O(n),几乎等于O(nlogn)。
【讨论】:
【参考方案7】:我已经回答了HERE
您还可以创建大小为last_element_in_the_existing_array + 1
的布尔数组。
在for
循环中标记现有数组中存在的所有元素true
。
在另一个for
循环中打印包含false
又名缺失的元素的索引。
时间复杂度:O(last_element_in_the_existing_array)
空间复杂度:O(array.length)
【讨论】:
【参考方案8】:如果范围提前给你,在这种情况下范围是 [1,10],你可以对你的范围和给你的数字执行 XOR 运算。由于 XOR 是可交换操作。您将剩下 3,6
(1 2 3 4 5 6 7 8 9 10) XOR (1 2 4 5 7 8 9 10) =3,6
【讨论】:
我不明白。您究竟如何对整个范围进行异或? @PlagueHammer 这可能是 bitset 方法。以上是关于缺少数字 面试问题 Redux的主要内容,如果未能解决你的问题,请参考以下文章