在未排序的数组中找到等于给定总和的2个数字

时间:2012-03-11 16:38:34

标签: algorithm language-agnostic

我们需要在数组中找到一对数,其总和等于给定值。

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

总和= 10 然后这些对是 - {6,4},{9,1}

我有两个解决方案。

  • 一个O(nlogn)解决方案 - 使用2个迭代器(开始和结束)排序+校验和。
  • 一个O(n)解决方案 - 对数组进行散列。然后检查哈希表中是否存在sum-hash[i]

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

所以,我想知道我们是否可以在 O(n)时间和 O(1)空间中进行。这不是功课!

18 个答案:

答案 0 :(得分:18)

使用就地基数排序和OP的第一个带有2个迭代器的解决方案,相互接近。

如果数组中的数字不是某种多精度数字并且例如是32位整数,则可以使用几乎没有额外空间(每次传递1位)在2 * 32次传递中对它们进行排序。或者2 * 8遍和16个整数计数器(每遍4位)。


2个迭代器解决方案的详细信息:

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

如果迭代器引用的元素总和小于所需的值,则前进第一个迭代器。如果它大于所需的值,则前进第二个迭代器。如果它等于所需的值,则成功。

只需要一次通过,因此时间复杂度为O(n)。空间复杂度为O(1)。如果使用基数排序,整个算法的复杂性是相同的。


如果您对相关问题感兴趣(总和超过2个数字),请参阅"Sum-subset with a fixed subset size""Finding three elements in an array whose sum is closest to an given number"

答案 1 :(得分:6)

这是来自微软亚洲研究院的经典访谈问题 如何在未排序的数组中找到等于给定总和的2个数字。

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

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

[3]使用Hash
基于[2]算法并使用哈希,时间复杂度可以减少到O(N),但是这个解决方案将添加哈希的O(N)空间。

[4]最优算法:

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.这个问题将变得非常复杂。

问题然后:
如何找到具有给定数字的所有组合案例?

这是一个经典的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

答案 2 :(得分:3)

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.

答案 3 :(得分:1)

如果您假设对的总和值M是常量并且数组中的条目是正数,那么您可以在一次传递中执行此操作(O(n)时间)使用M/2指针(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;
  } 

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

答案 4 :(得分:1)

<html>
    <head>
        <title>What you want to name the webpage</title>
    </head>
</html>

答案 5 :(得分:0)

创建一个包含Key的字典(列表中的数字),Value是获取所需值所需的数字。接下来,检查列表中是否存在数字对。

def check_sum_in_list(p_list, p_check_sum):
    l_dict = {i: (p_check_sum - i) for i in p_list}
    for key, value in l_dict.items():
        if key in p_list and value in p_list:
            return True
    return False


if __name__ == '__main__':
    l1 = [1, 3, 7, 12, 72, 2, 8]
    l2 = [1, 2, 2, 4, 7, 4, 13, 32]

    print(check_sum_in_list(l1, 10))
    print(check_sum_in_list(l2, 99))

Output:
True
Flase

第2版

import random


def check_sum_in_list(p_list, p_searched_sum):
    print(list(p_list))
    l_dict = {i: p_searched_sum - i for i in set(p_list)}
    for key, value in l_dict.items():
        if key in p_list and value in p_list:
            if p_list.index(key) != p_list.index(value):
                print(key, value)
                return True
    return False


if __name__ == '__main__':
    l1 = []
    for i in range(1, 2000000):
        l1.append(random.randrange(1, 1000))

    j = 0
    i = 9
    while i < len(l1):
        if check_sum_in_list(l1[j:i], 100):
            print('Found')
            break
        else:
            print('Continue searching')
            j = i
            i = i + 10

Output:
...
[154, 596, 758, 924, 797, 379, 731, 278, 992, 167]
Continue searching
[808, 730, 216, 15, 261, 149, 65, 386, 670, 770]
Continue searching
[961, 632, 39, 888, 61, 18, 166, 167, 474, 108]
39 61
Finded
[Finished in 3.9s]

答案 6 :(得分:0)

显而易见的解决方案是否无效(迭代每对连续)或两个数字是否为任何顺序?

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

答案 7 :(得分:0)

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

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

}

答案 8 :(得分:0)

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

答案 9 :(得分:0)

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解决方案。 “m”表示这两个数和的总和所需的目标值。 我相信这是最低的成本。 Erict2k使用了itertools.combinations,与我的算法相比,它也会花费相似或更高的时间和空间成本。

答案 10 :(得分:0)

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

答案 11 :(得分:0)

如果数组中的两个整数与比较整数匹配,则以下代码返回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;
                }
            }
        }
    }

答案 12 :(得分:0)

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

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 sum = 21 x = 11
1 2 3 9 11 20 || i = 0,j = 4 sum = 13 x = 11
1 2 3 9 11 20 || i = 0,j = 4 sum = 11 x = 11
END

答案 13 :(得分:0)

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

对数组进行排序。并从两端开始比较。

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)

答案 14 :(得分:0)

下面的代码将数组和数字N作为目标总和。 首先对数组进行排序,然后是一个包含该数组的新数组 拍摄剩余的元素,然后不通过二分搜索扫描 但同时对剩余部分和阵列进行简单扫描。

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

答案 15 :(得分:0)

`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)
*******/
}

答案 16 :(得分:0)

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

答案 17 :(得分:-1)

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