【问题标题】:Determine if array contains two elements which equal a certain sum?确定数组是否包含两个等于某个总和的元素?
【发布时间】:2012-09-28 07:06:45
【问题描述】:
// 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;
}

【问题讨论】:

  • 排序,然后从两端爬行。
  • 家庭作业?您的代码中有错误。如果您的输入是 [1,0],if (numbers[i] &lt; s) 将始终评估 false。事实上......您的代码永远不会接受零是产生匹配所需的第二个加数的情况(即 [1,0] 代表 sum = 1; [2,0] 代表 sum = 2; [5, 0, 0 ] for sum = 5; [1, 5, 0, 0] for sum = 5; 都将通过单元测试)。

标签: java performance algorithm


【解决方案1】:

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

  1. 从列表中创建一个以哈希为基础的集合,使其包含列表的所有元素。这需要 O(n)。
  2. 遍历列表中的每个元素n,计算s-n = d,并检查集合中是否存在d。如果d 存在,则n+d = s,因此返回true。如果您通过列表但没有找到合适的d,则返回false。这是通过您的列表一次性完成的,每次查找需要 O(1),所以这一步也需要 O(n)。

【讨论】:

  • 一个问题,如果 [5, 2] = 怎么办? 10
  • 最初该集合将包含 5 和 2。当您开始穿过 arra 时,它找到的第一个元素是“5”。然后你在集合中查找 5 和 .. 就可以了。在那里。所以它认为它找到了一个 5 并错误地返回 true。我想稍微修改一下就可以解决这个问题。使用地图而不是集合。然后记录每个数字。如果您发现它的计数大于 1,那么您就得到了答案。
  • 同意这是有效的,但它会处理重复吗?对于输入:{1, 1, 1, 5, 7, 9} 和预期总和:3?我不太明白“哈希支持”的术语。会是什么?哈希集?
  • 非常感谢。
【解决方案2】:

【讨论】:

    【解决方案3】:

    您可以通过对数组进行排序来解决这个问题,然后保留 2 个指向数组开头和结尾的指针,并通过移动两个指针来找到这 2 个数字。排序步骤耗时 O(nlog n),第二步耗时 O(n)。

    正如@Adam 所指出的,从数组中删除重复元素也很好,这样如果数组包含许多重复数字,您可以减少第二步的时间。

    至于第二步怎么做:

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

    为什么这样正确(我用右端表示大端,左端表示小端):

    • 如果 sum 大于 n,则使用右端没有意义,因为所有大于当前左端的数字都会使情况变得更糟。
    • 如果 sum 小于 n,则使用左端没有意义,因为所有小于当前右端的数字都会使情况变得更糟。
    • 在每个步骤中,我们将检查已删除数字和剩余数字之间的所有可能组合(逻辑上)。最后,我们将穷尽所有数字对之间所有可能的组合。

    【讨论】:

    • 好答案。我还将增强排序器以覆盖重复的条目 - 列表只需要是一个集合。这略微提高了第 2 步的性能,因为您不会重复测试相同的数字。但是,这会破坏您的列表。您可以改为保留一个查找表,计算排序期间列表中遇到的数字重复项的数量(但是,如果您使用分/治排序将不起作用) - 然后您知道在第 2 步期间要跳过多少元素。这不会实现一个数量级的改进,但可能会很重要,具体取决于您的数据集。
    【解决方案4】:

    这是一个考虑重复条目的解决方案。它是用 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;
    }
    

    享受吧!

    【讨论】:

      【解决方案5】:

      在 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);
      }
      

      如果只回答是不够的,还想知道A[i]+A[j]=target的i和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);
      

      【讨论】:

        猜你喜欢
        • 2019-04-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-16
        • 1970-01-01
        • 2015-04-16
        • 2012-02-22
        • 2018-03-22
        相关资源
        最近更新 更多