【问题标题】:Have O(n^2) algorithm for "two-sum", convert to O(n) linear solution [duplicate]具有“二和”的O(n ^ 2)算法,转换为O(n)线性解决方案[重复]
【发布时间】:2017-04-16 20:06:33
【问题描述】:

对于经典的二和问题,我有一个 O(n^2) 的解决方案。其中 A[1...n] 排序的正整数数组。 t 是某个正整数。

需要证明 A 包含两个不同的元素 a 和 b s.t。 a+ b = t

到目前为止,这是我的解决方案:

t = a number;
    for (i=0; i<A.length; i++)
          for each A[j]
            if A[i] + A[j] == t
                return true
    return false

如何使它成为线性解决方案? O(n) 挠头想弄清楚。

到目前为止,这是我想到的一种方法。 i 将从 A 的开头开始,j 将从 A 的末尾开始。 i 将递增,j 将递减。所以我将在 for 循环中有两个计数器变量 i 和 j。

【问题讨论】:

  • 到目前为止,您查找并尝试了什么?
  • 如果数组已排序:在开头放一个指针,最后放一个指针。第一个前进,另一个后退。
  • A[i] 的可能范围是多少?
  • 数组已排序,我只是添加了一种方法,使用两个计数器,一个向前,另一个向后。 A 的长度是任意的。比如说我们可以做 A[10]
  • 如果我的 O(n^2) 在这里正确?

标签: algorithm big-o


【解决方案1】:

有几种方法可以改进这一点。

  1. 您可以扩展您的算法,但您可以进行二分搜索,而不是对每个术语进行简单搜索
t = a number
for (i = 0; i < A.length; i++)
  j = binarySearch(A, t - A[i], i, A.length - 1)
  if (j != null) 
      return true
return false

二分查找由 O(log N) 步骤完成,因为您对数组中的每个元素执行二分查找,所以整个算法的复杂度为 O(N*log N) 这已经是对 O(N^2) 的巨大改进,但您可以做得更好。

  1. 我们以总和 11 和数组 1、3、4、8、9 为例。 您已经可以看到 (3,8) 满足总和。为了找到它,假设有两个指针,一旦指向数组的开头 (1),我们将其称为 H 并用 bold 表示它,另一个指向数组的末尾 ( 9),我们称它为 T 并用 emphasis 表示它。

1 3 4 8 9

现在两个指针的和是 1 + 9 = 10。 10 小于所需的总和 (11),无法通过移动 T 指针来达到所需的总和,因此我们将 H 指针向右移动:

1 3 4 8 9

3 + 9 = 12 大于期望的总和,没有办法通过移动 H 指针来达到期望的总和,向右移动将进一步增加总和,向左移动将我们带到初始状态,所以我们将 T 指针向左移动:

1 3 4 8 9

3 + 8 = 11

所以算法的规则包括向左移动 H 指针或向右移动 T 指针,当两个指针的和等于所需的和,或者 H 和 T 交叉(T 变小比 H)。

t = a number
H = 0
T = A.length - 1
S = -1

while H < T && S != t
    S = A[H] + A[T]
    if S < t
        H++
    else if S > t
        T--

return S == t

很容易看出这个算法的运行时间为 O(N),因为我们最多遍历每个元素一次。

【讨论】:

    【解决方案2】:

    您创建了 2 个包含索引 0 和索引 n-1 的新变量,我们分别称它们为 i 和 j。 然后,检查 A[i] 和 A[j] 的总和,如果总和小于 t,则增加 i(较低的索引),如果大于则减少 j(较高的索引)。继续直到你找到 i 和 j 使得 A[i] + A[j] = t 所以你返回 true,或者 j

    int i = 0, j = n-1;
    while(i < j) {
        if(A[i] + A[j] == t)
            return true;
        if(A[i] + A[j] < t)
            i++;
        else
            j--;
    return false;
    

    【讨论】:

    • 如果有办法使用 for 循环来做到这一点,并缩短它?
    • 我找不到使用 for 循环以任何方式缩短它的实现。
    【解决方案3】:

    鉴于 A[i] 相对较小(可能小于 10^6),您可以创建一个大小为 10^6 的数组 B,每个值都等于 0。然后应用以下算法:

    for i in 1...N:
        B[A[i]] += 1
    for i in 1...N:
        if t - A[i] > 0:
            if B[t-A[i]] > 0:
                return True
    

    编辑:好吧,既然我们知道数组已排序,那么寻找另一种算法可能更明智。我将把答案留在这里,因为它仍然适用于某类相关问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多