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

时间:2011-11-14 10:02:14

标签: java big-o binary-search

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

例如。给定[1,4,7,2,3,4]将得到总和8(1 + 7)但不是20

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

  

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

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

    为证明第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;
      }
    }
  }

}

5 个答案:

答案 0 :(得分:5)

与@ 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

答案 1 :(得分:4)

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

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

然后你有x=8
因此,您可以使用S'=[7,6,5,4,1]中的元素减去x来形成S 使用S'中的mergesort对O(nlogn)进行排序 删除重复项需要O(n)

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

答案 2 :(得分:3)

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的内存(你代表你的S)可以很容易地为0到2 ** 31的整数编写 O(n)而你的S'使用比特而不是像我在这里做的那样。)

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

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

答案 3 :(得分:0)

您的算法是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))。
  3. 编辑: 回答你的第一个问题。 x是您要查找的总和,y是输入集的元素。因此,如果S = {y_1,y_2,y_3,...,y_n},那么S'= {x - y_1,x - y_2,x - y_3,... x - y_n}

答案 4 :(得分:0)

它的工作原理如下:

  1. 对输入数组进行排序
  2. 创建两个变量front = 0 [start point] rear = array of end [end point]。
  3. 如果前后均为零增量,则从数组的两端开始计算它的总和
  4. 如果不增加包含更多元素的一面。[因为数组被排序,所以没有机会找到元素,使得它们的总和为0]
  5. 前面停止&gt; =后方。
  6. 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));
    }
    

    }