在一个未sorting的数组中查找2个数字等于给定的总和

我们需要在一个数组中find一对数字,其总和等于一个给定的数值。

A = {6,4,5,7,9,1,2} 

Sum = 10然后这些对是 – {6,4},{9,1}

我有两个解决scheme。

  • 一个O(nlogn)解决scheme – 对2个迭代器(开始和结束)进行sorting+校验和。
  • 一个O(n)解决scheme – 哈希arrays。 然后检查sum-hash[i]是否存在sum-hash[i]

但问题是,虽然第二个解决scheme是O(n)时间,但也使用O(n)空间。

所以,我想知道是否可以在O(n)时间和O(1)空间中做到这一点。 这不是功课!

使用就地基数sorting和OP的2个迭代器的第一个解决scheme,彼此相向。

如果数组中的数字不是某种多精度数字,例如32位整数,则可以使用几乎没有附加空间(每通过1位)的方式将它们排列为2 * 32遍。 或者2 * 8遍和16个整数计数器(每次4位)。


2迭代器解决scheme的详细信息:

第一个迭代器最初指向sorting数组的第一个元素,并向前推进。 第二个迭代器最初指向数组的最后一个元素并向后推进。

如果由迭代器引用的元素总和小于所需值,则提前第一个迭代器。 如果它大于所需值,则提前第二个迭代器。 如果它等于所要求的价值,那么成功。

只需要一次,所以时间复杂度是O(n)。 空间复杂度是O(1)。 如果使用基数sorting,整个algorithm的复杂性是相同的。


如果您对相关问题感兴趣(总和超过2个数字),请参阅“具有固定子集大小的求和子集”和“在总共最接近给定数字的数组中find三个元素” 。

这是来自微软亚洲研究院的经典面试问题。
如何find一个未分类数组中的2个数字等于给定的总和。

[1]蛮力解决scheme
这个algorithm很简单。 时间复杂度为O(N ^ 2)

[2]使用二进制search
使用二进制search来查找每个arr [i]的Sum-arr [i],时间复杂度可以减less到O(N * logN)

[3]使用哈希
基于[2]algorithm并使用哈希,时间复杂度可以减less到O(N),但是这个解决scheme会增加哈希的O(N)空间。

[4]最优algorithm:

Pseduo代码:

 for(i=0;j=n-1;i<j) if(arr[i]+arr[j]==sum) return (i,j); else if(arr[i]+arr[j]<sum) i++; else j--; return(-1,-1); 

要么

 If a[M] + a[m] > I then M-- If a[M] + a[m] < I then m++ If a[M] + a[m] == I you have found it If m > M, no such numbers exist. 

而且,这个问题完全解决了吗? 不可以。如果数字是N.这个问题将变得非常复杂。

那么问题:
我怎样才能find给定数量的所有组合案例?

这是一个经典的NP-Complete问题,被称为subset-sum。
要了解NP / NPC / NP-Hard,最好阅读一些专业书籍。

参考文献:
[1] http://www.quora.com/Mathematics/How-can-I-find-all-the-combination-cases-with-a-given-number
[2] http://en.wikipedia.org/wiki/Subset_sum_problem

 for (int i=0; i < array.size(); i++){ int value = array[i]; int diff = sum - value; if (! hashSet.contains(diffvalue)){ hashSet.put(value,value); } else{ printf(sum = diffvalue + hashSet.get(diffvalue)); } } -------- Sum being sum of 2 numbers. 

如果假设对的总和为M的值是常数,并且数组中的条目是正数,则可以使用M/2指针( O(1) O(n)时间) O(1)空间)如下。 指针标记为P1,P2,...,Pk ,其中k=floor(M/2) 。 然后做这样的事情

 for (int i=0; i<N; ++i) { int j = array[i]; if (j < M/2) { if (Pj == 0) Pj = -(i+1); // found smaller unpaired else if (Pj > 0) print(Pj-1,i); // found a pair Pj = 0; } else if (Pj == 0) Pj = (i+1); // found larger unpaired else if (Pj < 0) print(Pj-1,i); // found a pair Pj = 0; } } 

例如,您可以通过将索引存储为基数N数字来处理重复的条目(例如,两个6)。 对于M/2 ,您可以添加条件

  if (j == M/2) { if (Pj == 0) Pj = i+1; // found unpaired middle else print(Pj-1,i); // found a pair Pj = 0; } 

但是现在你有把这些对放在一起的问题。

  public void printPairsOfNumbers(int[] a, int sum){ //O(n2) for (int i = 0; i < a.length; i++) { for (int j = i+1; j < a.length; j++) { if(sum - a[i] == a[j]){ //match.. System.out.println(a[i]+","+a[j]); } } } //O(n) time and O(n) space Set<Integer> cache = new HashSet<Integer>(); cache.add(a[0]); for (int i = 1; i < a.length; i++) { if(cache.contains(sum - a[i])){ //match// System.out.println(a[i]+","+(sum-a[i])); }else{ cache.add(a[i]); } } } 

显而易见的解决scheme是不是工作(迭代每一个连续的对)还是两个数字以任何顺序?

在这种情况下,您可以对数字列表进行sorting,并使用随机抽样来对已sorting的列表进行分区,直到您有足够小的子列表进行迭代。

 public static ArrayList<Integer> find(int[] A , int target){ HashSet<Integer> set = new HashSet<Integer>(); ArrayList<Integer> list = new ArrayList<Integer>(); int diffrence = 0; for(Integer i : A){ set.add(i); } for(int i = 0; i <A.length; i++){ diffrence = target- A[i]; if(set.contains(diffrence)&&A[i]!=diffrence){ list.add(A[i]); list.add(diffrence); return list; } } return null; } 
 `package algorithmsDesignAnalysis; public class USELESStemp { public static void main(String[] args){ int A[] = {6, 8, 7, 5, 3, 11, 10}; int sum = 12; int[] B = new int[A.length]; int Max =A.length; for(int i=0; i<A.length; i++){ B[i] = sum - A[i]; if(B[i] > Max) Max = B[i]; if(A[i] > Max) Max = A[i]; System.out.print(" " + B[i] + ""); } // O(n) here; System.out.println("\n Max = " + Max); int[] Array = new int[Max+1]; for(int i=0; i<B.length; i++){ Array[B[i]] = B[i]; } // O(n) here; for(int i=0; i<A.length; i++){ if (Array[A[i]] >= 0) System.out.println("We got one: " + A[i] +" and " + (sum-A[i])); } // O(n) here; } // end main(); /****** Running time: 3*O(n) *******/ } 

下面的代码将数组和数目N作为目标总和。 首先对数组进行sorting,然后获取包含其余元素的新数组,然后通过二进制search对其进行扫描,但不能同时扫描余数和数组。

 public static int solution(int[] a, int N) { quickSort(a, 0, a.length-1); // nlog(n) int[] remainders = new int[a.length]; for (int i=0; i<a.length; i++) { remainders[a.length-1-i] = N - a[i]; // n } int previous = 0; for (int j=0; j<a.length; j++) { // ~~ n int k = previous; while(k < remainders.length && remainders[k] < a[j]) { k++; } if(k < remainders.length && remainders[k] == a[j]) { return 1; } previous = k; } return 0; } 

不应该从两端迭代解决问题?

sorting数组。 并从两端开始比较。

 if((arr[start] + arr[end]) < sum) start++; if((arr[start] + arr[end]) > sum) end--; if((arr[start] + arr[end]) = sum) {print arr[start] "," arr[end] ; start++} if(start > end) break; 

时间复杂度O(nlogn)

如果它是一个有序数组,我们只需要一对数字而不是所有的对,我们可以这样做:

 public void sums(int a[] , int x){ // A = 1,2,3,9,11,20 x=11 int i=0 , j=a.length-1; while(i < j){ if(a[i] + a[j] == x) system.out.println("the numbers : "a[x] + " " + a[y]); else if(a[i] + a[j] < x) i++; else j--; } } 

1 2 3 9 11 20 || i = 0,j = 5和= 21 x = 11
1 2 3 9 11 20 || i = 0,j = 4和= 13 x = 11
1 2 3 9 11 20 || i = 0,j = 4和= 11 x = 11
结束

如果数组中的两个整数匹配比较的整数,则以下代码返回true。

  function compareArraySums(array, compare){ var candidates = []; function compareAdditions(element, index, array){ if(element <= y){ candidates.push(element); } } array.forEach(compareAdditions); for(var i = 0; i < candidates.length; i++){ for(var j = 0; j < candidates.length; j++){ if (i + j === y){ return true; } } } } 

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 

https://github.com/clockzhong/findSumPairNumber

 #! /usr/bin/env python import sys import os import re #get the number list numberListStr=raw_input("Please input your number list (seperated by spaces)...\n") numberList=[int(i) for i in numberListStr.split()] print 'you have input the following number list:' print numberList #get the sum target value sumTargetStr=raw_input("Please input your target number:\n") sumTarget=int(sumTargetStr) print 'your target is: ' print sumTarget def generatePairsWith2IndexLists(list1, list2): result=[] for item1 in list1: for item2 in list2: #result.append([item1, item2]) result.append([item1+1, item2+1]) #print result return result def generatePairsWithOneIndexLists(list1): result=[] index = 0 while index< (len(list1)-1): index2=index+1 while index2 < len(list1): #result.append([list1[index],list1[index2]]) result.append([list1[index]+1,list1[index2]+1]) index2+=1 index+=1 return result def getPairs(numList, target): pairList=[] candidateSlots=[] ##we have (target-1) slots #init the candidateSlots list index=0 while index < target+1: candidateSlots.append(None) index+=1 #generate the candidateSlots, contribute O(n) complexity index=0 while index<len(numList): if numList[index]<=target and numList[index]>=0: #print 'index:',index #print 'numList[index]:',numList[index] #print 'len(candidateSlots):',len(candidateSlots) if candidateSlots[numList[index]]==None: candidateSlots[numList[index]]=[index] else: candidateSlots[numList[index]].append(index) index+=1 #print candidateSlots #generate the pairs list based on the candidateSlots[] we just created #contribute O(target) complexity index=0 while index<=(target/2): if candidateSlots[index]!=None and candidateSlots[target-index]!=None: if index!=(target-index): newPairList=generatePairsWith2IndexLists(candidateSlots[index], candidateSlots[target-index]) else: newPairList=generatePairsWithOneIndexLists(candidateSlots[index]) pairList+=newPairList index+=1 return pairList print getPairs(numberList, sumTarget) 

我已经在O(n + m)时间和空间成本下成功实现了一个Python解决scheme。 “m”表示这两个数字的总和所需要的目标值。 我相信这是最低的成本可以得到。 Erict2k使用itertools.combinations,它也将花费相似或更高的时间和空间成本比较我的algorithm。

如果数字不是很大,可以用快速傅里叶变换乘两个多项式,然后在O(1)中检查x ^(所需和)和之前的系数是否大于零。 O(n log n)总数!

//使用哈希的Java实现import java.io. *;

class PairSum {private static final int MAX = 100000; //最大的Hashmap大小

 static void printpairs(int arr[],int sum) { // Declares and initializes the whole array as false boolean[] binmap = new boolean[MAX]; for (int i=0; i<arr.length; ++i) { int temp = sum-arr[i]; // checking for condition if (temp>=0 && binmap[temp]) { System.out.println("Pair with given sum " + sum + " is (" + arr[i] + ", "+temp+")"); } binmap[arr[i]] = true; } } // Main to test the above function public static void main (String[] args) { int A[] = {1, 4, 45, 6, 10, 8}; int n = 16; printpairs(A, n); } 

}

  public static void Main(string[] args) { int[] myArray = {1,2,3,4,5,6,1,4,2,2,7 }; int Sum = 9; for (int j = 1; j < myArray.Length; j++) { if (myArray[j-1]+myArray[j]==Sum) { Console.WriteLine("{0}, {1}",myArray[j-1],myArray[j]); } } Console.ReadLine(); }