在排序数组中找到三个元素,它们与第四个元素相加

时间:2016-09-27 07:53:04

标签: arrays algorithm search

我的一位朋友最近得到了这个面试问题,这在我们看来是可以解决的,但不是在面试官认为应该可能的渐近时间范围内。这是问题所在:

  

您有一个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)元组,并返回有多少元组的计数。但我认为即使在所需的时间限制内找到其中一个也很难。

2 个答案:

答案 0 :(得分:3)

如果算法必须列出解决方案(即 a b c 的集合,以及满足条件的 d ,最坏的情况时间复杂度为 O(n 4

1。可以有 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 解决方案,算法可以有更好的时间复杂度。

2。算法

这是已发布算法的略微变化,不涉及 H 因子。它还描述了如何处理不同配置导致相同总和的情况。

  • 检索所有对并将其存储在数组 X 中,其中每个元素都会获得以下信息:

    a :两个中的最小索引 b :另一个指数
    总和xs[a] + xs[b]

  • 的值
  • 同时还为另一个阵列 Y 中的每一对存储,以下内容:

    c :两个中的最小索引 d :另一个指数
    总和xs[d] - xs[c]

  • 的值

上述操作的时间复杂度为 O(n²)

  • 按照元素的 sum 属性对两个数组进行排序。如果值相等,则排序顺序将如下确定:对于 X 数组,增加 b ;通过减少 c 来增加 Y 数组。可以在 O(n²) O(n²logn)时间内完成排序。

[编辑:我无法证明先前声明 O(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)。如果数组中的值范围很小,则可以执行此操作。散列函数的选择还取决于数组中元素的属性。