亚马逊采访中向我询问了这个问题 -
给定一个正整数数组,你必须找到无法从数组中的数字之和形成的最小正整数。
示例:
Array:[4 13 2 3 1]
result= 11 { Since 11 was smallest positive number which can not be formed from the given array elements }
我做的是:
但这是nlog(n)解决方案。
采访者对此并不满意,并在不到O(n log n)时间内提出解决方案。
答案 0 :(得分:38)
有一个漂亮的算法可以在时间O(n + Sort)中解决这个问题,其中Sort是对输入数组进行排序所需的时间。
算法背后的想法是对数组进行排序,然后提出以下问题:使用数组的前k个元素,你无法做出的最小正整数是多少?然后从左到右向前扫描数组,更新你对这个问题的答案,直到找到你不能做的最小数字。
这是它的工作原理。最初,您不能做的最小数字是1.然后,从左到右,执行以下操作:
candidate
)无法创建的最小数字,现在正在查看值A[k]
。因此,数字candidate - A[k]
必须是您可以使用数组的前k个元素创建的某个数字,否则candidate - A[k]
将小于您声称无法使用的最小数字candidate
。数组中的前k个数字。此外,您可以在candidate + A[k]
到A[k]
范围内输入任意数字,因为您可以从1到candidate - 1
的任意数字开头,然后添加{{ 1}}到它。因此,请将candidate
设置为candidate + A[k]
并增加k
。在伪代码中:
Sort(A)
candidate = 1
for i from 1 to length(A):
if A[i] > candidate: return candidate
else: candidate = candidate + A[i]
return candidate
这是[4, 13, 2, 1, 3]
的测试。对数组进行排序以获得[1, 2, 3, 4, 13]
。然后,将candidate
设置为1.然后执行以下操作:
candidate
= 1:
candidate
,因此请设置candidate = candidate + A[1] = 2
candidate
= 2:
candidate
,因此请设置candidate = candidate + A[2] = 4
candidate
= 4:
candidate
,因此请设置candidate = candidate + A[3] = 7
candidate
= 7:
candidate
,因此请设置candidate = candidate + A[4] = 11
candidate
= 11:
candidate
,请返回candidate
(11)。所以答案是11。
这里的运行时是O(n + Sort),因为在排序之外,运行时是O(n)。您可以使用heapsort清楚地在O(n log n)时间内进行排序,如果您知道数字的某些上限,则可以使用基数排序按时间排序O(n log U)(其中U是最大可能数)。如果U是固定常数(例如,10 9 ),则基数排序在时间O(n)中运行,然后整个算法也在时间O(n)中运行。
希望这有帮助!
答案 1 :(得分:8)
使用bitvectors在线性时间内完成此操作。
以空位向量b开头。然后对于数组中的每个元素k,执行以下操作:
b = b | b<< k | 2 ^(K-1)
要清楚,第i个元素设置为1表示数字i,| k
将第k个元素设置为1。
完成数组处理后,b中第一个零的索引就是你的答案(从右边算起,从1开始)。
第一个零:位置11。
答案 2 :(得分:7)
考虑区间[2 i .. 2 i + 1 - 1]中的所有整数。并且假设低于2 i 的所有整数可以由给定数组的数字之和形成。还假设我们已经知道C,它是低于2 i 的所有数字的总和。如果C> = 2 i + 1 -1,则该间隔中的每个数字可以表示为给定数字的总和。否则,我们可以检查区间[2 i .. C + 1]是否包含给定数组中的任何数字。如果没有这样的数字,C + 1就是我们搜索的内容。
以下是算法草图:
S[int_log(x)] += x
。foreach i: C[i] = C[i-1] + S[i]
。i = int_log(x) - 1; B[i] |= (x <= C[i] + 1)
。B[]
的相应元素。如果不明显为什么我们可以申请第3步,这是证据。选择2 i 和C之间的任意数字,然后按降序顺序从中减去2 i 以下的所有数字。最终我们得到的数字少于最后一个减去的数字或零。如果结果为零,只需将所有减去的数字加在一起,我们就可以得到所选数字的表示。如果结果为非零且小于最后一个减去的数字,则该结果也小于2 i ,因此它是“可表示的”,并且没有一个减去的数字用于其表示。当我们添加这些减去的数字时,我们有所选数字的表示。这也表明,不是逐个过滤间隔,我们可以通过直接跳转到C的int_log来一次跳过几个间隔。
时间复杂度由函数int_log()
确定,函数int_log()
是整数对数或数字中最高设置位的索引。如果我们的指令集包含整数对数或其任何等价(计数前导零或具有浮点数的技巧),则复杂度为O(n)。否则,我们可以使用一些黑客来在O(日志日志U)中实现Input: [ 4 13 2 3 1] [ 1 2 3 9] [ 1 1 2 9]
int_log: 2 3 1 1 0 0 1 1 3 0 0 1 3
int_log: 0 1 2 3 0 1 2 3 0 1 2 3
S: 1 5 4 13 1 5 0 9 2 2 0 9
C: 1 6 10 23 1 6 6 15 2 4 4 13
filtered(C): n n n n n n n n n n n n
number in
[2^i..C+1]: 2 4 - 2 - - 2 - -
C+1: 11 7 5
并获得O(n * log log U)时间复杂度。 (这里U是数组中最大的数字)。
如果步骤1(除了更新总和)也将更新给定范围内的最小值,则不再需要步骤4。我们可以将C [i]与Min [i + 1]进行比较。这意味着我们只需要单次传递输入数组。或者我们可以将此算法应用于数组而不是数字流。
几个例子:
{{1}}
对于多精度输入数,此方法需要O(n * log M)时间和O(log M)空间。其中M是数组中的最大数字。只需读取所有数字就需要同一时间(在最坏的情况下,我们需要它们的每一个数字)。
这个结果仍然可以改进为O(n * log R),其中R是该算法找到的值(实际上是它的输出敏感变体)。这种优化所需的唯一修改不是一次处理整数,而是逐位处理它们:第一次传递处理每个数字的低位(如位0..63),第二次传递 - 下一位(如64..127)等我们可以在找到结果后忽略所有高阶位。这也减少了对O(K)数的空间要求,其中K是机器字中的位数。
答案 3 :(得分:0)
如果您对数组进行排序,它将对您有用。计数排序本可以在 O(n)
中完成,但如果您认为在实际上很大的场景中,范围可能相当大。
Quicksort O(n*logn)
将为您完成这项工作:
def smallestPositiveInteger(self, array):
candidate = 1
n = len(array)
array = sorted(array)
for i in range(0, n):
if array[i] <= candidate:
candidate += array[i]
else:
break
return candidate
答案 4 :(得分:-1)
https://www.geeksforgeeks.org/find-smallest-value-represented-sum-subset-given-array/ 在这个问题中,因为它与询问数组已被排序的问题类似。之后,最佳的运行时复杂度是O(n)。因此@templatetypedef提供的解决方案将起作用。