确定数组是否包含两个等于某个总和的元素?

时间:2012-10-08 03:07:24

标签: java performance algorithm

// Checks whether the array contains two elements whose sum is s.
// Input: A list of numbers and an integer s
// Output: return True if the answer is yes, else return False

public static boolean calvalue (int[] numbers, int s){
for (int i=0; i< numbers.length; i++){
    for (int j=i+1; j<numbers.length;j++){
        if (numbers[i] < s){
            if (numbers[i]+numbers[j] == s){
                return true;
                }
            }
        }
    }
    return false;
}

5 个答案:

答案 0 :(得分:12)

这可以在O(n)中实现。

  1. 从列表中创建一个哈希支持的集合,使其包含列表的所有元素。这需要O(n)。
  2. 浏览列表中的每个元素n,计算s-n = d,然后检查集合中是否存在d。如果存在d,则n+d = s,则返回true。如果您在未找到合适的d的情况下通过列表,请返回false。这是通过列表的单次传递实现的,每次查找都采用O(1),因此这一步也需要O(n)。

答案 1 :(得分:7)

答案 2 :(得分:5)

你可以通过对数组进行排序来解决这个问题,然后保持2个指针指向数组的开始和结束,并通过移动两个指针找到2个数字。排序步骤采用O(nlog n),第二步采用O(n)。

正如@Adam指出的那样,从阵列中删除重复的元素也是一件好事,这样如果数组中包含许多重复的数字,你可以减少第二步的时间。

关于如何进行第二步:

  • 如果当前2个数字的总和大于n,则将指针向后移动。
  • 如果当前2个数字的总和小于n,则将指针向前移动。
  • 当两个指针指向同一个元素时停止并拒绝。如果sum等于n,则接受。

为什么这是正确的(我使用右端表示较大的末端,左端表示较小的末端):

  • 如果sum大于n,则使用右端没有意义,因为所有大于当前左端的数字都会使其变差。
  • 如果sum小于n,则使用左端没有意义,因为所有小于当前右端的数字都会使其变差。
  • 在每一步中,我们将在删除的数字和剩余的数字之间经历所有可能的组合(逻辑上)。最后,我们将耗尽所有可能的组合数。

答案 3 :(得分:1)

这是一个考虑重复条目的解决方案。它是用javascript编写的,并假设数组已排序。

var count_pairs = function(_arr,x) {
  if(!x) x = 0;
  var pairs = 0;
  var i = 0;
  var k = _arr.length-1;
  if((k+1)<2) return pairs;
  var halfX = x/2; 
  while(i<k) {
    var curK = _arr[k];
    var curI = _arr[i];
    var pairsThisLoop = 0;
    if(curK+curI==x) {
      // if midpoint and equal find combinations
      if(curK==curI) {
        var comb = 1;
        while(--k>=i) pairs+=(comb++);
        break;
      }
      // count pair and k duplicates
      pairsThisLoop++;
      while(_arr[--k]==curK) pairsThisLoop++;
      // add k side pairs to running total for every i side pair found
      pairs+=pairsThisLoop;
      while(_arr[++i]==curI) pairs+=pairsThisLoop;
    } else {
      // if we are at a mid point
      if(curK==curI) break;
      var distK = Math.abs(halfX-curK);
      var distI = Math.abs(halfX-curI);
      if(distI > distK) while(_arr[++i]==curI);
      else while(_arr[--k]==curK);
    }
  }
  return pairs;
}

享受!

答案 4 :(得分:0)

在Java中

    private static boolean find(int[] nums, long k, int[] ids) {
    // walk from both sides towards center.
    // index[0] keep left side index, index[1] keep right side index,
    // runtime O(N)
    int l = ids[0];
    int r = ids[1];
    if (l == r) {
        ids[0] = -1;
        ids[1] = -1;
        return false;
    }
    if (nums[l] + nums[r] == k) {
        ids[0]++;
        ids[1]++;
        return true;
    }
    if (nums[l] + nums[r] < k) {
        ids[0]++;
    } else {
        ids[1]--;
    }
    return find(nums, k, ids);
}

public static boolean twoSum(final int[] nums, int target) {
    // Arrays.sort(nums); //if the nums is not sorted, then sorted it firstly, thus the running time will be O(NlogN)
    int[] ids = new int[2];
    ids[0] = 0;
    ids[1] = nums.length - 1;
    return find(nums, target, ids);
}

测试

@Test(timeout = 10L, expected = Test.None.class)
public void test() {
    Assert.assertEquals( twoSum(new int[]{3, 2, 4}, 6), true);
    Assert.assertEquals( twoSum(new int[]{3, 2, 4}, 8), false);
}

如果只有答案是不够的,并想知道哪一个是i和j,那么A [i] + A [j] =目标

以@cheeken的想法如下,如果有重复的数字,首先出现一个。

   public static int[] twoSum2(final int[] nums, int target) {
    int[] r = new int[2];
    r[0] = -1;
    r[1] = -1;
    Set<Integer> set = new HashSet<>(nums.length);
    Map<Integer, List<Integer>> map = new HashMap<>(nums.length);
    for (int i = 0; i < nums.length; i++) {
        int v = nums[i];
        if (set.contains(target - v)) {
            r[0] = map.get(target - v).get(0);
            r[1] = i;
            return r;
        }
        set.add(v);
        List ids = map.get(v);
        if (ids == null) {
            ids = new LinkedList<>();
            ids.add(i);
            map.put(v, ids);

        } else {
            ids.add(i);
            map.put(v, ids);
        }
    }
    return r;
}

测试

    int[] r = twoSum2(new int[]{3, 2, 4}, 6);
    Assert.assertEquals(r[0], 1);
    Assert.assertEquals(r[1], 2);
    r =  twoSum2(new int[]{3, 2, 4}, 8);
    Assert.assertEquals(r[0], r[1]);
    Assert.assertEquals(r[1], -1);