找到添加到给定总和的数组中的数字对

时间:2011-12-01 00:17:50

标签: arrays performance algorithm processing-efficiency

问题:给定一个未排序的正整数数组,是否有可能从该数组中找到一对总和达到给定总和的整数?

约束:这应该在O(n)和就地(没有任何外部存储,如数组,哈希映射)完成(你可以使用额外的变量/指针)

如果无法做到这一点,是否可以提供相同的证明?

18 个答案:

答案 0 :(得分:53)

如果你有一个排序数组,你可以通过向中间移动两个指针在O(n)中找到这样的一对

i = 0
j = n-1
while(i < j){
   if      (a[i] + a[j] == target) return (i, j);
   else if (a[i] + a[j] <  target) i += 1;
   else if (a[i] + a[j] >  target) j -= 1;
}
return NOT_FOUND;

如果您对数字的大小有界限(或者如果数组已经排在第一位),则可以对O(N)进行排序。即使这样,log n因子也很小,我不想费心去除它。

<强>证明:

如果有解决方案(i*, j*),请假设ii*到达j之前达到j*而不失一般性。对于j'j*之间的所有j,我们知道a[j'] > a[j*]我们可以推断a[i] + a[j'] > a[i*] + a[j*] = target,因此,算法的所有后续步骤都将导致j减少直到达到j*(或相等的值)而不给i前进并“错过”解决方案的机会。

另一方面的解释是相似的。

答案 1 :(得分:12)

适用于已排序数组的O(N)时间和O(1)空间解决方案:

M成为您追求的价值。使用两个指针XY。从头开始X=0,在结尾开始Y=N-1。计算总和sum = array[X] + array[Y]。如果sum > M,则递减Y,否则递增X。如果指针交叉,则不存在解决方案。

您可以对一般数组进行排序,但我不确定通常会有O(N)时间和O(1)空间解决方案。

答案 2 :(得分:3)

如果数组包含数字,这可能是可能的,数字的上限是事先知道的。然后使用计数排序或基数排序(o(n))并使用@PengOne建议的算法。

否则 我想不出O(n)解决方案。但O(nlgn)解决方案的工作原理如下: -

首先使用合并排序或快速排序(对于inplace)对数组进行排序。查找sum - array_element是否在此排序数组中。 可以使用二进制搜索。

So total time complexity: O(nlgn) + O(lgn) -> O(nlgn).

答案 3 :(得分:3)

AS @PengOne提到它在一般方案中是不可能的。但是如果你对i / p数据做了一些限制。

  1. 所有元素都是+或全部 - 如果没有,则需要知道范围(高,低)并进行更改。
  2. K,与一般元素相比,两个整数之和稀疏。
  3. 可以销毁i / p阵列A [N]。
  4. 步骤1:将小于SUM的所有元素移动到数组的开头,比如N通道我们将数组分成[0,K]&amp; [K,N-1]使得[0,K]包含元素&lt; = SUM。

    步骤2:由于我们知道边界(0到SUM),我们可以使用基数排序。

    步骤3:在A [K]上使用二进制搜索,一个好处是,如果我们需要找到互补元素,我们只需要查看数组A [K]的一半。所以在A [k]中我们迭代A [0到K / 2 + 1]我们需要在A [i到K]中进行二分搜索。

    总的appx。时间是,N + K + K / 2 lg(K)其中K是元素数btw 0到Sum in i / p A [N]

    注意:如果你使用@ PengOne的方法,你可以用K做第3步。所以总时间是N + 2K,绝对是O(N)

    我们不使用任何额外的内存,但会破坏i / p阵列,这也不错,因为它没有任何排序开始。

答案 4 :(得分:2)

这是一个O(N)算法。它依赖于an in-place O(N) duplicate removal algorithm,并且在数组中存在一个良好的哈希函数。

首先,从阵列中删除所有重复项。

其次,遍历数组,并用min(x,S-x)替换每个数字x,其中S是您想要达到的总和。

第三,找出数组中是否有重复项:如果'x'重复,那么原始数组中必须出现'x'和'S-x',并且找到了你的对。

答案 5 :(得分:2)

  1. 使用count sort对数组O(n)进行排序。
  2. 取两个指针,一个从数组的第0个索引开始,另一个从数组末尾开始说(n-1)。

    运行循环直到低<=高

    Sum = arr[low] + arr[high]  
    if(sum == target)
           print low, high
    if(sum < target)
           low++
    if(sum > target)
           high--
    

    步骤2到10需要O(n)时间,并且计数排序需要O(n)。总时间复杂度为O(n)。

答案 6 :(得分:2)

以下网站提供了一个简单的解决方案,使用hashset查看数字,然后在hashset中搜索给定的sum-current数 http://www.dsalgo.com/UnsortedTwoSumToK.php

答案 7 :(得分:2)

首先,使用radix sort对数组进行排序。那会让你回到O(kN)。然后继续@PengOne建议。

答案 8 :(得分:1)

这是一个考虑重复条目的解决方案。它是用javascript编写的,使用排序和未排序的数组运行。该解决方案在O(n)时间内运行。

var count_pairs_unsorted = function(_arr,x) {
  // setup variables
  var asc_arr = [];
  var len = _arr.length;
  if(!x) x = 0;
  var pairs = 0;
  var i = -1;
  var k = len-1;
  if(len<2) return pairs;
  // tally all the like numbers into buckets
  while(i<k) {
    asc_arr[_arr[i]]=-(~(asc_arr[_arr[i]]));
    asc_arr[_arr[k]]=-(~(asc_arr[_arr[k]]));
    i++;
    k--;
  }
  // odd amount of elements
  if(i==k) {
    asc_arr[_arr[k]]=-(~(asc_arr[_arr[k]]));
    k--;
  }
  // count all the pairs reducing tallies as you go
  while(i<len||k>-1){
    var y;
    if(i<len){
      y = x-_arr[i];
      if(asc_arr[y]!=undefined&&(asc_arr[y]+asc_arr[_arr[i]])>1) {
        if(_arr[i]==y) {
          var comb = 1;
          while(--asc_arr[_arr[i]] > 0) {pairs+=(comb++);}
        } else pairs+=asc_arr[_arr[i]]*asc_arr[y];
        asc_arr[y] = 0;
        asc_arr[_arr[i]] = 0;
      }

    }
    if(k>-1) {
      y = x-_arr[k];
      if(asc_arr[y]!=undefined&&(asc_arr[y]+asc_arr[_arr[k]])>1) {
        if(_arr[k]==y) {
          var comb = 1;
          while(--asc_arr[_arr[k]] > 0) {pairs+=(comb++);}
        } else pairs+=asc_arr[_arr[k]]*asc_arr[y];
        asc_arr[y] = 0;
        asc_arr[_arr[k]] = 0;
      }

    }
    i++;
    k--;
  }
  return pairs;
}

从阵列的两侧开始,慢慢向内工作,记住每个号码被发现的次数。一旦到达中点,所有数字都会被计算出来,您现在可以在进行计数时对两个指针进行计数。

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

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

享受!

答案 9 :(得分:1)

Ruby实现

ar1 = [ 32, 44, 68, 54, 65, 43, 68, 46, 68, 56]
for i in 0..ar1.length-1
 t  = 100 - ar1[i]
  if ar1.include?(t)
    s= ar1.count(t)
    if s < 2
      print   s , " - " ,  t, " , " , ar1[i]  , " pair ", i, "\n"
    end
  end
end

答案 10 :(得分:1)

在javascript中:当n大于n时,此代码会增加时间和迭代次数。程序执行的测试数将等于((n *(n / 2)+ n / 2),其中n是元素数。如果(arr [i] + arr [j ] === 0),其中0可以是任意数字。

var arr = [-4, -3, 3, 4];          
                var lengtharr = arr.length;        
                var i = 0;                         
                var j = 1;                         
                var k = 1;                          
                do {                                                    
                    do {
                        if (arr[i] + arr[j] === 0) { document.write(' Elements arr [' + i + '] [' + j + '] sum 0'); } else { document.write('____'); }
                     j++;
                    } while (j < lengtharr);        
                    k++;
                    j = k;
                    i++;
                } while (i < (lengtharr-1));        

答案 11 :(得分:0)

这是python中的解决方案:

a = [9, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 21, 8, 9, 2, 9, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 2, 8, 9, 2, 2, 8,
     9, 2, 15, 11, 21, 8, 9, 12, 2, 8, 9, 2, 15, 11, 21, 7, 9, 2, 23, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 12, 9, 2, 15, 11, 21, 8, 9, 2, 2,
     8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 7.12, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9,
     2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 21, 8, 0.87, 78]
i = 0
j = len(a) - 1
my_sum = 8
finded_numbers = ()
iterations = 0
while(OK):
    iterations += 1
    if (i < j):
        i += 1
    if (i == j):
        if (i == 0):
            OK = False
            break
        i = 0
        j -= 1
    if (a[i] + a[j] == my_sum):
        finded_numbers = (a[i], a[j]) 
        OK = False
print finded_numbers
print iterations

答案 12 :(得分:0)

不保证是可能的;如何选择给定的金额?

示例:未排序的整数数组

2, 6, 4, 8, 12, 10

给定总和:

7

...

答案 13 :(得分:0)

在接受采访时我被问到同样的问题,这是我想到的计划。还有一个改进,允许负数,但只需要修改索引。空间方面并不好,但我相信这里的运行时间是O(N)+ O(N)+ O(N的子集) - &gt;上)。我可能错了。

void find_sum(int *array_numbers, int x){
 int i, freq, n_numbers; 
 int array_freq[x+1]= {0}; // x + 1 as there could be 0’s as well
 if(array_numbers)
 {
  n_numbers = (int) sizeof(array_numbers);
  for(i=0; i<n_numbers;i++){ array_freq[array_numbers[i]]++; } //O(N) 
  for(i=0; i<n_numbers;i++) 
  { //O(N) 
   if ((array_freq[x-array_numbers[i]] > 0)&&(array_freq[array_numbers[i]] > 0)&&(array_numbers[i]!=(x/2)))
   { 
    freq = array_freq[x-array_numbers[i]] * array_freq[array_numbers[i]];
    printf(“-{%d,%d} %d times\n”,array_numbers[i],x-array_numbers[i],freq ); 
    // “-{3, 7} 6 times” if there’s 3 ‘7’s and 2 ‘3’s
    array_freq[array_numbers[i]]=0;
    array_freq[x-array_numbers[i]]=0; // doing this we don’t get them repeated
   }
  } // end loop
  if ((x%2)=0)
  {
   freq = array_freq[x/2];
   n_numbers=0;
   for(i=1; i<freq;i++)
   { //O([size-k subset])
    n_numbers+= (freq-i); 
   } 
   printf(“-{%d,%d} %d times\n”,x/2,x/2,n_numbers);
  }
  return;
 }else{
 return; // Incoming NULL array 
 printf(“nothing to do here, bad pointer\n”);
 }
}

欢迎评论家。

答案 14 :(得分:0)

在java中,这取决于数组中的最大数量。 它返回一个具有两个元素索引的int []。 它是O(N)。

  public static int[] twoSum(final int[] nums, int target) {
    int[] r = new int[2];
    r[0] = -1;
    r[1] = -1;
    int[] vIndex = new int[0Xffff];
    for (int i = 0; i < nums.length; i++) {
        int delta = 0Xfff;
        int gapIndex = target - nums[i] + delta;
        if (vIndex[gapIndex] != 0) {
            r[0] = vIndex[gapIndex];
            r[1] = i + 1;
            return r;
        } else {
            vIndex[nums[i] + delta] = i + 1;
        }
    }
    return r;
}

答案 15 :(得分:0)

首先你应该找到reverse array =&gt;总和减去实际数组 然后检查实际数组中是否存在来自这些新数组的任何元素。

const arr = [0, 1, 2, 6];

const sum = 8;

let isPairExist = arr
  .map(item => sum - item) // [8, 7, 6, 2];
  .find((item, index) => {
    arr.splice(0, 1); // an element should pair with another element
    return arr.find(x => x === item);
  })
  ? true : false;

console.log(isPairExist);

答案 16 :(得分:0)

使用O(n×n)性能的Naïve双循环打印输出可以使用O(n)内存用于哈希表,如下所示,可以提高线性O(n)性能:

void TwoIntegersSum(int[] given, int sum)
{
    Hashtable ht = new Hashtable();
    for (int i = 0; i < given.Length; i++)
        if (ht.Contains(sum - given[i]))
            Console.WriteLine("{0} + {1}", given[i], sum - given[i]);
        else
            ht.Add(given[i], sum - given[i]);
    Console.Read();
}

答案 17 :(得分:0)

def pair_sum(arr,k):
    counter = 0
    lookup = set()
    for num in arr:
        if k-num in lookup:
            counter+=1
        else:
            lookup.add(num)
    return counter
    pass
pair_sum([1,3,2,2],4)

python中的解决方案