我的一位朋友最近得到了这个面试问题,这在我们看来是可以解决的,但不是在面试官认为应该可能的渐近时间范围内。这是问题所在:
您有一个
N
整数数组xs
,已排序但可能不同。您的目标是找到四个数组索引(1)(a,b,c,d)
,以便保持以下两个属性:xs[a] + xs[b] + xs[c] = xs[d] a < b < c < d
目标是在O(N 2 )时间内完成此任务。
首先,O(N 3 log(N))解决方案是显而易见的:对于每个(a,b,c)
有序三元组,使用二进制搜索来查看是否有合适的d
被发现。现在,如何做得更好?
面试官的一个有趣建议是将第一个条件重写为:
xs[a] + xs[b] = xs[d] - xs[c]
此后不清楚该怎么做,但也许我们可以选择一个支点值P,并搜索一个(a,b)
对,加起来为P,一对(d,c)
对减去它。对于给定的P,通过从数组的两端向内搜索,该搜索很容易在O(n)时间内完成。然而,在我看来,问题是有N 2 这样的值P,而不仅仅是N,所以我们实际上根本没有减少问题的大小:我们和#39;做O(N)工作,O(N 2 )次。
我们发现在其他地方在线讨论了一些相关问题:Find 3 numbers in an array adding to a given sum在N 2 时间内是可解的,但要求总和提前确定;适应相同的算法,但迭代每个可能的总和使我们一如既往地在N 3 。
另一个相关的问题似乎是Find all triplets in array with sum less than or equal to given sum,但我不确定这里有多少相关的东西:不平等而不是平等混合了很多东西,当然,目标是固定的而不是变化的。
那么,我们缺少什么?考虑到性能要求,问题毕竟是不可能的吗?或者是否有一个我们无法发现的聪明算法?
(1)实际上提出的问题是找到所有这样的(a,b,c,d)
元组,并返回有多少元组的计数。但我认为即使在所需的时间限制内找到其中一个也很难。
答案 0 :(得分:3)
如果算法必须列出解决方案(即 a , b , c 的集合,以及满足条件的 d ,最坏的情况时间复杂度为 O(n 4 ):
这个简单的例子是一个只有0值的数组。然后 a , b , c 和 d 只要保持有序就具有所有自由。这表示 O(n 4 )解决方案。
但更常见的是遵循以下模式的数组具有 O(n 4 )解决方案:
w, w, w, ... x, x, x, ..., y, y, y, ... z, z, z, ....
每次出现次数最多,并且:
w + x + y = z
但是,要仅生成 number 解决方案,算法可以有更好的时间复杂度。
这是已发布算法的略微变化,不涉及 H 因子。它还描述了如何处理不同配置导致相同总和的情况。
检索所有对并将其存储在数组 X 中,其中每个元素都会获得以下信息:
a :两个中的最小索引
b :另一个指数
总和:xs[a] + xs[b]
同时还为另一个阵列 Y 中的每一对存储,以下内容:
c :两个中的最小索引
d :另一个指数
总和:xs[d] - xs[c]
上述操作的时间复杂度为 O(n²)
[编辑:我无法证明先前声明 O(n²)(除非做出一些允许基数/桶分类算法的假设,我不会假设)。如注释中所述,一般情况下,n²元素的数组可以按 O(n²logn²)进行排序, O(n²logn),但是不是 O(n²)]
通过“tandem”中的两个数组来查找相等的和。如果是这种情况,则需要检查X[i].b < Y[j].c
。如果是这样,它就代表了一个解但是可能有很多,并且在可接受的时间内计算它们需要特别小心。
设m = n(n-1)/2
,即数组 X 中的元素数量(也是数组 Y 的大小):
i = 0
j = 0
while i < m and j < m:
if X[i].sum < Y[j].sum:
i = i + 1
elif X[i].sum > Y[j].sum:
j = j + 1
else:
# We have a solution. Need to count all others that have same sums in X and Y.
# Find last match in Y and set k as index to it:
countY = 0
while k < m and X[i].sum == Y[j].sum and X[i].b < Y[j].c:
countY = countY + 1
j = j + 1
k = j - 1
# add chunks to `count`:
while i < m and countY >= 0 and X[i].sum == Y[k].sum:
while countY >= 0 and X[i].b >= Y[k].c:
countY = countY - 1
k = k - 1
count = count + countY
i = i + 1
请注意,尽管存在嵌套循环,但变量 i 仅会增加, j 也会增加。变量 k 总是在最里面的循环中递减。虽然它也可以从较高的值开始,但它永远不能通过 k 索引多次处理相同的 Y 元素,因为在递减此索引时,它保持在 Y 的“相同总和”范围内。
所以这意味着算法的最后一部分在 O(m)中运行, O(n²)。由于我的最新编辑确认排序步骤不是 O(n²),该步骤决定了整体时间复杂度: O(n²logn)。
答案 1 :(得分:2)
所以一个解决方案可以是:
列出所有可能的x [a] + x [b]值,使得&lt; b并以这种方式散列它们
key = (x[a]+x[b]) and value = (a,b).
此步骤的复杂性 - O(n ^ 2)
现在列出所有x [d] - x [c]值,使得d> C。此外,对于每个x [d] - x [c],通过查询搜索哈希映射中的条目。如果存在c> 1的条目,我们有一个解决方案。 b任何命中。 该步骤的复杂性 - O(n ^ 2)* H.
其中H是hashmap中的搜索时间。
总复杂度 - O(n ^ 2)* H.现在H可以是O(1)。如果数组中的值范围很小,则可以执行此操作。散列函数的选择还取决于数组中元素的属性。