查找数组中总和为指定值的所有整数对

时间:2009-09-29 18:18:13

标签: algorithm

  

设计一种算法来查找数组中所有整数对,它们总和为指定值。

我尝试使用哈希表存储数组元素总和的条目,但这不是一个有效的解决方案。

我可以用什么算法有效地解决这个问题?

15 个答案:

答案 0 :(得分:32)

我不明白为什么哈希表方法效率低下,至少在算法分析术语中 - 在内存位置术语中无可否认,它可能非常糟糕。无论如何,扫描阵列两次......

第一次扫描 - 将所有数组元素放在哈希表中 - 总共O(n)。单个插入仅按摊销O(1),但关于摊销分析如何工作的一个巧妙的事情意味着O(n)是绝对的 - 不摊销。

第二次扫描 - 检查哈希表中的(sum - current) - 总共O(n)。

这至少在理论上击败了O(n log n)排序和搜索方法。

然后,请注意您可以将两个扫描合并为一个。在第一次扫描期间遇到第二对时,您可以发现一对。在伪代码......

for i in array.range
  hashset.insert (array [i])

  diff = sum - array [i]
  if hashset.includes (diff)
    output diff, array [i]

如果您需要项目的位置,请使用散列图并在其中存储项目位置。如果需要处理重复项,则可能需要在哈希映射中存储计数。对于位置和重复项,您可能需要一个起始指针的散列图,用于链接的位置列表。

这假设了哈希表的实现,但是在大多数当前语言和库中通常的实现都是相当安全的。

BTW - 组合扫描不应被视为优化。迭代开销应该是微不足道的。对于非常大的数组,内存局部性问题可能会使单个传递稍微有效,但实际的内存局部性问题无论如何都会在哈希表查找中。

IMO是组合扫描的唯一真正原因是因为您只希望每对报告一次 - 以双扫描方式处理它会更麻烦。

答案 1 :(得分:18)

  1. 如果数组已排序:

    设i = 0,j =数组结束,sum =您要查找的值, 然后做:

    如果i + j = sum,则输出(i,j)。
    如果i + j<总和,然后将我移动到正确的位置 如果i + j>求和,然后将j移到左边一个位置。

    时间复杂度:O(n)。空间复杂度:O(1)。

  2. 如果数组未排序,有几种方法可以解决此问题:

    1. 对数组进行排序,然后使用上述方法。

    2. HashMap:

      将所有元素存储在HashMap中。

      a+b=sum,所以b=sum-a。对于数组的每个元素a,从HashMap中查找b

      HashMap查找需要分摊O(1)。

      时间复杂度:O(n)。空间复杂度:O(n)。

    3. <强>位图:

      迭代输入以创建位图,其中每个位对应于元素值。假设输入为{2,5,8},然后我们将位图数组的索引2,5和8从二进制0切换为1.每个元素需要O(1),因此总共为O(n)。

      再次浏览输入。我们知道b=sum-a,因此对于输入中的每个元素a,请查找其b,这可以在O(1)中完成,因为它是位图索引。这也总共需要O(n)。

      时间复杂度:O(n)+ O(n)= O(n)。空间复杂度:位图空间= O(n)。

答案 2 :(得分:12)

您甚至不需要将所有元素存储在hashmap中,然后进行扫描。您可以在第一次迭代期间进行扫描。

void foo(int[] A, int sum) {
    HashSet<Integer> set = new HashSet<Integer>();
    for (int e : A) {
        if (set.contains(sum-e)) {
            System.out.println(e + "," + (sum-e));
            // deal with the duplicated case
            set.remove(sum-e);
        } else {
            set.add(e);
        }
    }
}

答案 3 :(得分:6)

如何排序数组,然后从两端进入?

答案 4 :(得分:5)

假设所需的sum = R

  1. 对数组进行排序
  2. 对于数组A(n)中的每个数字,进行二分搜索以找到数A(x),使得A(n)+ A(x)= R

答案 5 :(得分:2)

如果您不介意在空间中花费O(M)M是您要求的总和,则可以在O(N + M)时间内执行此操作。在sums[i] = 1上一次过i <= M后设置N,然后在(sums[i] && sums[M-i])上单次检查M/2。{/ p>

答案 6 :(得分:1)

#include <iostream>
using namespace std;

#define MAX 15

int main()
{
 int array[MAX] = {-12,-6,-4,-2,0,1,2,4,6,7,8,12,13,20,24};
 const int find_sum = 0;
 int max_index = MAX - 1;
 int min_index = 0;
 while(min_index < max_index)
 {
  if(array[min_index] + array[max_index-min_index] == find_sum)
  {
   cout << array[min_index] << " & " << array[max_index-min_index] << " Matched" << endl;
   return 0;
  }
  if(array[min_index]+array[max_index-min_index] < find_sum)
  {
   min_index++;
   //max_index++;
  }
  if(array[min_index]+array[max_index-min_index] > find_sum)
  {
   max_index--;
  }
 }
 cout << "NO MATCH" << endl;
 return 0;
}
//-12 & 12 matched

答案 7 :(得分:1)

在Python 2.7中实现:

import itertools
list = [1, 1, 2, 3, 4, 5,]
uniquelist = set(list)
targetsum = 5
for n in itertools.combinations(uniquelist, 2):
    if n[0] + n[1] == targetsum:
    print str(n[0]) + " + " + str(n[1])

输出:

1 + 4
2 + 3

答案 8 :(得分:0)

这是一个考虑重复条目的解决方案。它是用javascript编写的,并假设数组已排序。该解决方案在O(n)时间内运行,除变量外不使用任何额外的内存。

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

所以这里适合所有人。

从数组的两侧开始,慢慢向内工作,确保重复计算(如果存在)。

它只计算对,但可以重新编写

  • 找到对
  • 找到对&lt; x
  • 查找对&gt; X

如果它是最好的答案,请尽情享受吧!

答案 9 :(得分:0)

考虑重复的解决方案,只使用一次每个数字:

void printPairs(int[] numbers, int S) {
    // toMap(numbers) converts the numbers array to a map, where
    // Key is a number from the original array
    // Value is a count of occurrences of this number in the array
    Map<Integer, Integer> numbersMap = toMap(numbers); 

    for (Entry<Integer, Integer> entry : numbersMap.entrySet()) {
      if (entry.getValue().equals(0)) {
        continue;
      }
      int number = entry.getKey();
      int complement = S - number;
      if (numbersMap.containsKey(complement) && numbersMap.get(complement) > 0) {
      for (int j = 0; j < min(numbersMap.get(number), 
                              numbersMap.get(complement)); j++) {
        if (number.equals(complement) && numbersMap.get(number) < 2) {
           break;
        }
        System.out.println(number, complement);
        numbersMap.put(number, numbersMap.get(number) - 1);
        numbersMap.put(complement, numbersMap.get(complement) - 1);
      }
    }
  }
}

答案 10 :(得分:0)

Hashtable解决方案,在Ruby中(非常简单易懂):

</li>

答案 11 :(得分:0)

我们可以用C ++ STL map解决这个问题

void subsetSum(int arr[], int n, int sum)
{
    map<int, int>Map;

    for(int i=0; i<n; i++)
    {
        Map[arr[i]]++;
        if(Map.count(sum-arr[i]))
        {
            cout<<arr[i]<<" "<<sum-arr[i]<<"\n";
        }
    }
}

答案 12 :(得分:0)

@Test
public void hasPairWithSum() {
  assertFalse(hasPairWithSum_Ordered_Logarithmic(new int[] { 1, 2, 3, 9 }, 8));
  assertTrue(hasPairWithSum_Ordered_Logarithmic(new int[] { 1, 2, 4, 4 }, 8));

  assertFalse(hasPairWithSum_Ordered_Linear(new int[] { 1, 2, 3, 9 }, 8));
  assertTrue(hasPairWithSum_Ordered_Linear(new int[] { 1, 2, 4, 4 }, 8));

  assertFalse(hasPairWithSum_Unsorted_Linear(new int[] { 9, 1, 3, 2 }, 8));
  assertTrue(hasPairWithSum_Unsorted_Linear(new int[] { 4, 2, 1, 4 }, 8));

  assertFalse(hasPairWithSum_Unsorted_Quadratic(new int[] { 9, 1, 3, 2 }, 8));
  assertTrue(hasPairWithSum_Unsorted_Quadratic(new int[] { 4, 2, 1, 4 }, 8));
}

private boolean hasPairWithSum_Ordered_Logarithmic(int[] data, int sum) {
  for (int i = 0; i < data.length; i++) {
    int current = data[i];
    int complement = sum - current;
    int foundIndex = Arrays.binarySearch(data, complement);
    if (foundIndex >= 0 && foundIndex != i) {
      return true;
    }
  }
  return false;
}

private boolean hasPairWithSum_Ordered_Linear(int[] data, int sum) {
  int low = 0;
  int high = data.length - 1;
  while (low < high) {
    int total = data[low] + data[high];
    if (total == sum) {
      return true;
    } else if (total < sum) {
      low++;
    } else {
      high--;
    }
  }
  return false;
}

private boolean hasPairWithSum_Unsorted_Linear(int[] data, int sum) {
  Set<Integer> complements = Sets.newHashSet();
  for (int current : data) {
    if (complements.contains(current)) {
      return true;
    }
    complements.add(sum - current);
  }
  return false;
}

private boolean hasPairWithSum_Unsorted_Quadratic(int[] data, int sum) {
  for (int i = 0; i < data.length; i++) {
    int current = data[i];
    int complement = sum - current;
    for (int j = 0; j < data.length; j++) {
      if (data[j] == complement && i != j) {
        return true;
      }
    }
  }
  return false;
}

答案 13 :(得分:0)

创建哈希表,然后在其中查找值。

function sum_exist(num : number, arr : any[]) {
    var number_seen = {};
    for(let item of arr){
        if(num - item in number_seen){
            return true
        }
        number_seen[item] = 0;
    }
    return false;
}

测试用例(使用Jest)

test('Given a list of numbers, return whether any two sums equal to the set number.', () => {
    expect(sum_exist(17 , [10, 15, 3, 7])).toEqual(true);
});


test('Given a list of numbers, return whether any two sums equal to the set number.', () => {
    expect(sum_exist(16 , [10, 15, 3, 7])).toEqual(false);
});

答案 14 :(得分:0)

#python 3.x
def sum_pairs(list_data, number):    
    list_data.sort()
    left = 0
    right = len(list_data)-1
    pairs = []
    while left < right:        
        if list_data[left]+list_data[right] == number:
            find_pairs = [list_data[left], list_data[right]]
            pairs.append(find_pairs)
            right = right-1
        elif list_data[left]+list_data[right] < number:
            left = left+1
        else:
            right = right-1
    return pairs