【问题标题】:Find 2 numbers in an unsorted array equal to a given sum在未排序的数组中找到等于给定总和的 2 个数字
【发布时间】:2012-03-28 05:53:52
【问题描述】:

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

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) 空间内完成。这不是家庭作业!

【问题讨论】:

  • 数字需要连续???
  • 我怀疑这种算法是否存在......
  • 在这里说连续,我的意思是他们是否需要按照给定的顺序相互跟随???..在你的例子中..[6,4] 和 [9,1] 是在一个序列中.....4 是 6 的邻居,1 是 9 的邻居......
  • @TedHopp:如果您创建哈希集并检查同一迭代中是否存在元素,则可以轻松解决您描述的问题。 [而不是首先创建一个完整的哈希集,然后才检查一对]
  • 我不认为它可以用 O(1) 空间 约束来完成。

标签: algorithm language-agnostic


【解决方案1】:

明显的解决方案是否不起作用(迭代每个连续的对)或者这两个数字是否按任何顺序?

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

【讨论】:

  • 排序为O(nlogn),遍历连续对失败,例如:{9,3,1} sum 为 10。
  • @Blender 迭代对 = O(n^2)
  • @amit: 根据输入数字的约束,排序可能为 O(n)
  • 我不确定是否存在 O(n) 解决方案占用 O(1) 空间来处理如此复杂的事情。
  • @amit 对于无限范围使用O(n*sqrt(loglogn)) 的最佳排序算法是什么?
【解决方案2】:

如果您假设假设对的值 M 是恒定的,并且数组中的条目是正数,那么您可以使用 M/2 一次性完成此操作(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;
  } 

但现在你遇到了将这些对放在一起的问题。

【讨论】:

  • 不应该Pj更像P[j]吗?
  • 有了这些假设,我们不能只创建一个大小为 M 的直方图/集合的数组吗?
  • 如果M 是常数,我有一个更简单的算法。让M/2 在数组上传递,在第一次传递时查找 1 和 M-1,在第二次传递时查找 2 和 M-2,依此类推,时间为 O(NM) = O(N)。
  • M 是 O(1) 似乎是在作弊。如果 M 是 O(1),那么数组中将只有 O(1) 唯一值可以参与求和,因此您只需要 O(n) 时间来查找这些值,然后使用任何算法将是 O(1) 时间和空间。
【解决方案3】:

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

如果数组中的数字不是某种多精度数字,而是例如 32 位整数,您可以在 2*32 遍中对它们进行排序,几乎不需要额外的空间(每遍 1 位)。或者 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"

【讨论】:

  • 我很难说基数排序实际上需要 O(N) 时间,因为它取决于机器字的大小。对于固定机器,它是 O(N),但更一般地是 O(N log U),其中 U 是可表示的最大可能整数。
  • @EvgenyKluev :似乎非常接近所需的内容。但是基数排序是 O(kn),并不比 O(nlogn) 好。你能解释一下你是怎么称呼它 O(n) 的吗?
  • @EvgenyKluev : 如果每个键都是 9 位数字,那么基数的复杂度会很高
  • @templatetypedef,@NiteeshMehra,是的,一般情况下基数排序是 O(N log U)。此外,计算哈希函数是 O(log U)。填充和使用哈希表是 O(N log U)。由于 OP 的第二个解决方案假设复杂度为 O(N),因此我们有 O(N log U) = O(N)。或 O(U) = 1。这允许我假设 O(N) 进行基数排序。这不合理吗?无论如何,如果 O(U) > 1,我们根本无法获得 O(N) 解,因为我们必须处理 N log U 位(最坏情况)或 N log N 位(平均而言,对于均匀分布的数字和 N
  • @NiteeshMehra,如果每个密钥是 9 个十进制数字,它包含大约 32 位。您可以在 16 遍中对其进行排序(如我的回答中所述),或者您可以在 8 遍中进行排序,每遍 8 位和 256 个计数器。在实践中,如果 N 很大,它可能比比较排序的 O(N log N) 更好,或者如果 N 很小,它可能会更糟。
【解决方案4】:
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.

【讨论】:

  • 散列和你的解决方案有什么区别?
  • 没有他的仍然是 O(N) 空间
  • OP 明确表示没有多余的空间。为什么这个答案被赞成?
  • 用 array=[ 1, 2, 10, 3, 4] 和 sum = 20 试试
【解决方案5】:

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

[1]暴力破解
这个算法非常简单。时间复杂度为 O(N^2)

[2]使用二分查找
使用二叉搜索找到每个arr[i]的Sum-arr[i],时间复杂度可以降低到O(N*logN)

[3]使用哈希
基于[2]算法并使用hash,时间复杂度可以降低到O(N),但是这个方案会增加O(N)的hash空间。

[4]最优算法:

伪代码:

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 完全问题,称为子集和。
要了解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

【讨论】:

  • 您的第二个解决方案不正确。我们如何在未排序的数组中进行二分查找?
  • 也是第四个!这是不正确的!您的数组未排序,您不能增加或减少索引。你可能会错过其中的一些。
  • @Hengameh:如果我们先对数组进行排序,则第二种解决方案是正确的。请注意,使用适当的排序算法,运行时间仍然是 O(N*logN)
  • @Hengameh:如果我们先对数组进行排序,第四个解决方案也是正确的。显然,在这种情况下,运行时间将不再是 O(N),而是 O(N*LogN)。
  • 为什么 4 比 3 更优? O(N) 更好! 4 仅在空间受限时才有意义。
【解决方案6】:
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;
}

【讨论】:

    【解决方案7】:
    `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)
    *******/
    }
    

    【讨论】:

      【解决方案8】:

      以下代码将数组和数字 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;
      }
      

      【讨论】:

        【解决方案9】:

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

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

        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)

        【讨论】:

        • 排序使其成为 O(nlogn)。问题要求 O(n)。
        【解决方案10】:

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

        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
        结束

        【讨论】:

          【解决方案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;
                          }
                      }
                  }
              }
          

          【讨论】:

            【解决方案12】:
                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]);
                    }
                }
            
            }
            

            【讨论】:

              【解决方案13】:

              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
              

              【讨论】:

              • 我可以看到符号的轻松。愿意争论空间和时间复杂性吗?关于以O(n) time and O(1) space?结尾的问题
              【解决方案14】:

              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,与我的算法相比,它也会花费相似或更高的时间和空间成本。

              【讨论】:

                【解决方案15】:

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

                【讨论】:

                  【解决方案16】:
                      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();
                      }
                  

                  【讨论】:

                    【解决方案17】:

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

                    类 PairSum { 私有静态最终 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);
                    }
                    

                    }

                    【讨论】:

                      【解决方案18】:

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

                      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]
                      

                      【讨论】:

                      • 如果您添加几行代码来解释您的代码如何回答问题,那就太好了。
                      • 在代码中发现了一个错误。 key 和 value 为 5 时,添加切片分片搜索。
                      猜你喜欢
                      • 1970-01-01
                      • 2012-07-27
                      • 1970-01-01
                      • 2017-06-06
                      • 2014-05-26
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2015-09-17
                      相关资源
                      最近更新 更多