繁体   English   中英

O(n log(n))算法,检查int []中给定数字的2个数的和

[英]O(n log(n)) algorithm that checks if sum of 2 numbers in a int[] = given number

我应该创建一个O(n log(n))算法,检查int [] ==给定数字中是否有2个数字的总和。

例如。 鉴于[1,4,7,2,3,4]将有8(1 + 7)而不是20

给出的答案建议使用二进制排序或合并排序,但他们只给出了合并排序算法,而没有逻辑处理这个特殊要求。 然后另一个答案是:

假设x是我们要检查的总和,z是此数组中的元素集:以下算法解决了以下问题:

  1. 在S中排序元素
  2. 形成集合S'= {z:z = x-y表示某些y∈S}。
  3. 对S'中的元素进行排序。
  4. 如果S中的任何值出现多次,则删除除一个实例外的所有值。 为S'做同样的事情。
  5. 合并两个有序集S和S'。
  6. S中存在两个元素,当且仅当相同的值出现在合并输出的连续位置时,其总和才是x。

为了证明步骤4中的声明的合理性,首先观察如果在合并输出中出现两次任何值,它必须出现在连续的位置。 因此,我们可以在步骤5中重述该条件,因为在S中存在两个元素,当且仅当相同的值在合并输出中出现两次时,其总和恰好为x。 假设某个值w出现两次。 然后在S中出现一次,在S'出现一次。 因为w出现在S'中,所以存在一些y∈S,使得w = x-y,或x = w + y。 由于w∈S,元素w和y在S中并且与x相加。

相反,假设存在值w,y∈S使得w + y = x。 然后,由于x-y = w,值w出现在S'中。 因此,w在S和S'中,因此它将在合并输出中出现两次。

步骤1和3需要O(n log n)步骤。 步骤2,4,5和6需要O(n)步骤。 因此总运行时间为O(n log n)。

但我真的不是他们的意思。 在第2步中,x和y是什么?

但是我自己在下面创建,我想知道它的O(n log(n))

class FindSum {

  public static void main(String[] args) {
    int[] arr = {6,1,2,3,7,12,10,10};
    int targetSum = 20;

    Arrays.sort(arr);
    System.out.println(Arrays.toString(arr));
    int end = arr.length - 1;
    if (FindSum.binarySearchSum(arr, targetSum, 0, end, 0, end)) {
      System.out.println("Found!");
    } else {
      System.out.println("Not Found :(");
    }
  } 

  public static boolean binarySearchSum(int[] arr, int targetSum,
                                        int from1, int end1,
                                        int from2, int end2) {
    // idea is to use 2 "pointers" (simulating 2 arrays) to (binary) search 
    // for target sum
    int curr1 = from1 + (end1-from1)/2;
    int curr2 = from2 + (end2-from2)/2;
    System.out.print(String.format("Looking between %d to %d, %d to %d: %d, %d", from1, end1, from2, end2, curr1, curr2));
    int currSum = arr[curr1] + arr[curr2];
    System.out.println(". Sum = " + currSum);

    if (currSum == targetSum) { 
      // base case
      return true;
    } else if (currSum > targetSum) { 
      // currSum more than targetSum
      if (from2 != end2) { 
        // search in lower half of 2nd "array"
        return FindSum.binarySearchSum(arr, targetSum, from1, end1, from2, curr2 - 1);
      } else if (from1 != end2) { 
        // search in lower half of 1st "array" (resetting the start2, end2 args)
        return FindSum.binarySearchSum(arr, targetSum, from1, curr1 - 1, 0, arr.length - 1);
      } else {
        // can't find
        return false;
      }
    } else {
      // currSum < targetSum
      if (from2 != end2) { 
        // search in upper half of 2nd "array"
        return FindSum.binarySearchSum(arr, targetSum, from1, end1, curr2 + 1, end2);
      } else if (from1 != end2) { 
        // search in upper half of 1st "array" (resetting the start2, end2 args)
        return FindSum.binarySearchSum(arr, targetSum, curr1 + 1, end1, 0, arr.length - 1);
      } else {
        // can't find
        return false;
      }
    }
  }

}

与@ user384706类似,但您可以在O(n)执行此操作。

他们所说的如下:S = [1,4,7,2,3,4]

将这些添加到HashSet,理想情况下是TIntHashSet(但时间复杂度相同)

int total = 9;
Integer[] S = {1, 4, 7, 2, 3, 4, 6};
Set<Integer> set = new HashSet<Integer>(Arrays.asList(S));
for (int i : set)
    if (set.contains(total - i))
        System.out.println(i + " + " + (total - i) + " = " + total);

版画

2 + 7 = 9
3 + 6 = 9
6 + 3 = 9
7 + 2 = 9

他们说的是以下内容:
S=[1,4,7,2,3,4]

使用mergesort对S进行排序,得到Ss=[1,2,3,4,7] 成本是O(nlogn) - 只需检查维基。

然后你有x=8
所以你通过用S的元素减去x形成S'=[7,6,5,4,1]
使用O(nlogn) mergesort对S'排序
删除重复项需要O(n)

然后你合并SsS'
检查O(n)中连续位置的重复项。
总计是:
O(nlogn)+O(nlogn)+O(n)+O(n)+O(n) = O(nlogn)

O(n)解决方案怎么样?

从您的问题中不清楚您是否应该使用您所描述的“另一个答案”[原文如此]或者您是否可以提出自己的解决方案。

你应该问的第一件事是“有什么要求?” 因为有局限性。 您将获得的最大整数数是多少? 两百万? 一千万? 这些整数的范围是多少? 在你的问题中,他们似乎总是大于零。 这些整数的最大值是多少? 你可以使用多少内存?

因为总是有权衡。

例如,这是针对您的问题的非优化(见下文) O(n)解决方案(我在您的输入中添加了'8'):

@Test
public void testIt() {
    final int max = 10000000;
    final int[] S = new int[max+1];
    final int[] in = { 1, 4, 3, 2, 4, 7, 8 };
    for ( final int i : in ) {
        S[i]++;
    }
    assertFalse( containsSum(S, 1) );
    assertFalse( containsSum(S, 2) );
    assertTrue( containsSum(S, 3) );
    assertTrue( containsSum(S, 4) );
    assertTrue( containsSum(S, 5) );
    assertTrue( containsSum(S, 6) );
    assertTrue( containsSum(S, 7) );
    assertTrue( containsSum(S, 8) );
    assertTrue( containsSum(S, 9) );
    assertTrue( containsSum(S, 10) );
    assertTrue( containsSum(S, 11) );
    assertFalse( containsSum(S, 13) );
    assertFalse( containsSum(S, 14) );
    assertTrue( containsSum(S, 12) );
    assertTrue( containsSum(S, 15) );
    assertFalse( containsSum(S, 16) );
}

private static boolean containsSum( final int[] ar, final int sum ) {
    boolean found = false;
    for (int i = 1; i < sum && !found; i++) {
            final int b = sum - i;
            found = i == b ? ar[i] > 1 : ar[i] > 0 && ar[b] > 0;
    }
    return found;
}

它是非优化的,因为使用“仅”1 GB的内存(使用位而不是像ints代表你的S和你的S',可以很容易地编写一个O(n) ,用于从0到2 ** 31的整数。我在这做了)。

当然,人们可能会认为“但1GB是一个很大的记忆” :但这一切都取决于要求。 我上面的解决方案(或它的优化版本)是O(n)并且可以立即解决由1亿个整数组成的输入,其中任何其他解决方案都会失败(因为你有OutOfMemory错误,因为Java对象的开销)。

首先要问的是“有什么要求?” 您需要有关输入的更多信息,因为它始终是一种权衡。

它的工作原理如下:

  1. 对Input数组进行排序
  2. 创建两个变量front = 0 [start point] rear =数组的长度[end point]。
  3. 它从数组的两端开始计算它的总和,如果前后均为零增量
  4. 如果不增加包含更大元素的那一边。[因为数组被排序,所以没有机会找到它们的和为0的元素]
  5. 当前> =后方时停止。

public class TwoSumFaster {

private static int countTwoSum(int[] numbers) {
    int count = 0;
    int front = 0, rear = numbers.length - 1;

    while (front < rear) {
        if (numbers[front] + numbers[rear] == 0) {
            front++;
            rear--;
            count++;
        } else {
            if (Math.abs(numbers[front]) > Math.abs(numbers[rear])) {
                front++;
            } else {
                rear--;
            }
        }
    }

    return count;
}

public static void main(String[] args) {
    int[] numbers = { 1, 3, 5, 7, 12, 16, 19, 15, 11, 8, -1, -3, -7, -8, -11, -17, -15 };
    Arrays.sort(numbers); 
    System.out.println(countTwoSum(numbers));
}

}

您的算法是O(n log n)。 每次将第一个数组大小除以2或在第二个数组上进行二进制搜索。 这是O((log n)^ 2)最坏的情况(即S = {1,2 ...,n}和x = 0),因此它被排序吸收。

无论如何,你可以通过以下方式在O(n log n)中更容易地做到:

  1. 排序数组(O(n log n))
  2. 迭代它的元素(O(n)),同时在每次迭代中对当前元素的X补码进行二进制搜索(O(log n))。

编辑:回答您的第一个问题。 x是您要查找的总和,y是输入集的元素。 因此,如果S = {y_1,y_2,y_3,...,y_n},则S'= {x - y_1,x - y_2,x - y_3,... x - y_n}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM