35

Question: Given an unsorted array of positive integers, is it possible to find a pair of integers from that array that sum up to a given sum?

Constraints: This should be done in O(n) and in-place (without any external storage like arrays, hash-maps) (you can use extra variables/pointers)

If this is not possible, can there be a proof given for the same?

miku
  • 181,842
  • 47
  • 306
  • 310
noMAD
  • 7,744
  • 19
  • 56
  • 94
  • I can only think of a way with an external array. i think it's impossible in O(n) time. – Jakob Weisblat Dec 01 '11 at 00:20
  • 8
    @RomanB.: This is not my homework. I am studying for my interviews and this just came across my mind. – noMAD Dec 01 '11 at 00:21
  • Can be done if the initial array is ordered. If it is not ordered, you'll need O(N*N); or O(n log n) + O(n) by sorting it first. – wildplasser Dec 01 '11 at 00:25
  • Related but different: [Design an algorithm to find all pairs of integers within an array which sum to a specified value?](http://stackoverflow.com/questions/1494130/design-an-algorithm-to-find-all-pairs-of-integers-within-an-array-which-sum-to-a) – PengOne Dec 01 '11 at 00:30
  • Is spawning `n` threads to each do the search in `O(n)` time out of the question (assuming you have `n` processors that can read your memory)? :) – corsiKa Dec 01 '11 at 00:31
  • all I can come up with is O(n) with a hashtable – Jean-Bernard Pellerin Dec 01 '11 at 00:34
  • With no external storage and O(n) I think it's not possible. Being ordered first makes it easier, but as others stated, complexity would be higher. If you don't know anything about the data you're about to handle, you must go over the array at least a couple of times necessarily IMHO. – frarees Dec 01 '11 at 00:38
  • possible duplicate of [Given an unsorted array, find any two elements in the array whose sum is equal to the sum entered by the user](http://stackoverflow.com/questions/4259515/given-an-unsorted-array-find-any-two-elements-in-the-array-whose-sum-is-equal-t) – PengOne Dec 01 '11 at 00:40
  • Thanks for all your replies. I could only figure out how to do it with a HashMap in O(n) or if the array is sorted then PengOne's method. The things is, is there any way to show that it is not possible? Some kind of proof? – noMAD Dec 01 '11 at 01:48
  • I have an O(n) in-place algorithm, posted below. – Paul Hankin Jan 11 '14 at 09:28

20 Answers20

55

If you have a sorted array you can find such a pair in O(n) by moving two pointers toward the middle

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;

The sorting can be made O(N) if you have a bound on the size of the numbers (or if the the array is already sorted in the first place). Even then, a log n factor is really small and I don't want to bother to shave it off.

proof:

If there is a solution (i*, j*), suppose, without loss of generality, that i reaches i* before j reaches j*. Since for all j' between j* and j we know that a[j'] > a[j*] we can extrapolate that a[i] + a[j'] > a[i*] + a[j*] = target and, therefore, that all the following steps of the algorithm will cause j to decrease until it reaches j* (or an equal value) without giving i a chance to advance forward and "miss" the solution.

The interpretation in the other direction is similar.

hugomg
  • 68,213
  • 24
  • 160
  • 246
  • 1
    Sorry, but what do you mean by "sorting can be made O(n) if you have a bound on the size"? To sort an array, the most efficient algorithm will take O(NlogN), isn't it? Please give more explaination. Thanks. – user3068966 Dec 21 '13 at 06:00
  • @user3068966: The O(NlogN) bound is for algorithms that are only allowed to compare numbers with each other and swap their positions. Things like CountingSort or RadixSort can be O(N) because they are not comparison-based algorithms. – hugomg Dec 21 '13 at 12:52
  • shouldn't it be a[i] + a[j'] > a[i*] + a[j*], since in your proof i would = i* and a[j'] is > a[j*] – Clinton Jan 11 '14 at 01:54
  • @Clinton: oops! fixed – hugomg Jan 11 '14 at 09:06
  • 1
    @missingno: As I understand, the problem requires to be solved in-place. I'm not familiar with Counting Sort algo, but Radix Sort definitely is not in place algo. – sergeyan Mar 05 '14 at 13:35
  • This is not a proof that Solution is not possible in O(N) time and O(1) space,assuming bounds on size is not known. – Deepankar Singh May 30 '16 at 17:54
  • I don't quite understand `a[i] + a[j'] > a[i*] + a[j*]`. According to the question: a[j*] < a[j'], but a[i] < a[i*], isn't it? – dk123 Sep 20 '18 at 02:23
12

An O(N) time and O(1) space solution that works on a sorted array:

Let M be the value you're after. Use two pointers, X and Y. Start X=0 at the beginning and Y=N-1 at the end. Compute the sum sum = array[X] + array[Y]. If sum > M, then decrement Y, otherwise increment X. If the pointers cross, then no solution exists.

You can sort in place to get this for a general array, but I'm not certain there is an O(N) time and O(1) space solution in general.

PengOne
  • 48,188
  • 17
  • 130
  • 149
  • 1
    If we know the upper limit on the elements of the array, one can use radix or counting sort (O(n)) and apply the algorithm which you stated. – niting112 Dec 04 '11 at 10:46
5

My solution in Java (Time Complexity O(n)), this will output all the pairs with a given sum

import java.util.HashMap;
import java.util.Map;

public class Test {
public static void main(String[] args) {
    // TODO Auto-generated method stub
    Map<Integer, Integer> hash = new HashMap<>();
    int arr[] = {1,4,2,6,3,8,2,9};
    int sum = 5;
    for (int i = 0; i < arr.length; i++) {
        hash.put(arr[i],i);
    }
    
    for (int i = 0; i < arr.length; i++) {
        if(hash.containsKey(sum-arr[i])){
            //System.out.println(i+ " " +  hash.get(sum-arr[i]));
             System.out.println(arr[i]+ " " + (sum-arr[i]));
        }
    }
}
}
vaquar khan
  • 10,864
  • 5
  • 72
  • 96
Chetan Kole
  • 137
  • 2
  • 7
3

This might be possible if the array contains numbers, the upper limit of which is known to you beforehand. Then use counting sort or radix sort(o(n)) and use the algorithm which @PengOne suggested.

Otherwise I can't think of O(n) solution.But O(nlgn) solution works in this way:-

First sort the array using merge sort or quick sort(for inplace). Find if sum - array_element is there in this sorted array. One can use binary search for that.

So total time complexity: O(nlgn) + O(lgn) -> O(nlgn).
niting112
  • 2,498
  • 2
  • 16
  • 18
3

AS @PengOne mentioned it's not possible in general scheme of things. But if you make some restrictions on i/p data.

  1. all elements are all + or all -, if not then would need to know range (high, low) and made changes.
  2. K, sum of two integers is sparse compared to elements in general.
  3. It's okay to destroy i/p array A[N].

Step 1: Move all elements less than SUM to the beginning of array, say in N Passes we have divided array into [0,K] & [K, N-1] such that [0,K] contains elements <= SUM.

Step 2: Since we know bounds (0 to SUM) we can use radix sort.

Step 3: Use binary search on A[K], one good thing is that if we need to find complementary element we need only look half of array A[K]. so in A[k] we iterate over A[ 0 to K/2 + 1] we need to do binary search in A[i to K].

So total appx. time is, N + K + K/2 lg (K) where K is number of elements btw 0 to Sum in i/p A[N]

Note: if you use @PengOne's approach you can do step3 in K. So total time would be N+2K which is definitely O(N)

We do not use any additional memory but destroy the i/p array which is also not bad since it didn't had any ordering to begin with.

d1val
  • 401
  • 4
  • 4
2

The following site gives a simple solution using hashset that sees a number and then searches the hashset for given sum-current number http://www.dsalgo.com/UnsortedTwoSumToK.php

2

Here's an O(N) algorithm. It relies on an in-place O(N) duplicate removal algorithm, and the existence of a good hash function for the ints in your array.

First, remove all duplicates from the array.

Second, go through the array, and replace each number x with min(x, S-x) where S is the sum you want to reach.

Third, find if there's any duplicates in the array: if 'x' is duplicated, then 'x' and 'S-x' must have occurred in the original array, and you've found your pair.

Community
  • 1
  • 1
Paul Hankin
  • 54,811
  • 11
  • 92
  • 118
2
  1. Use count sort to sort the array O(n).
  2. take two pointers one starts from 0th index of array, and another from end of array say (n-1).

    run the loop until low <= high

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

    Step 2 to 10 takes O(n) time, and counting sort takes O(n). So total time complexity will be O(n).

Jay
  • 207
  • 2
  • 12
2

In javascript : This code when n is greater then the time and number of iterations increase. Number of test done by the program will be equal to ((n*(n/2)+n/2) where n is the number of elements.The given sum number is discarded in if (arr[i] + arr[j] === 0) where 0 could be any number given.

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));        
EJISRHA
  • 17
  • 3
2

First off, sort the array using radix sort. That'll set you back O(kN). Then proceed as @PengOne suggest.

Miquel
  • 15,405
  • 8
  • 54
  • 87
1

Here is a solution witch takes into account duplicate entries. It is written in javascript and runs using sorted and unsorted arrays. The solution runs in O(n) time.

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

Start at both side of the array and slowly work your way inwards keeping a count of how many times each number is found. Once you reach the midpoint all numbers are tallied and you can now progress both pointers counting the pairs as you go.

It only counts pairs but can be reworked to

  • find the pairs
  • find pairs < x
  • find pairs > x

Enjoy!

Drone Brain
  • 419
  • 3
  • 8
  • This solution assumes the array is sorted, which is not the question. Sorting takes O(n logn) and would defeat your argument that it runs in constant time. :) Thanks anyways – noMAD Apr 01 '13 at 14:53
  • A LSD radix sort takes O(k*n) and runs in place where c is the max number of digits. A hashmapping can have similar results although it does not run in place. So it is possible to pair this with a faster sort to get O(n) time on average. – Drone Brain Apr 04 '13 at 12:28
  • Hey noMAD. I've edited the solution and updated it with an algorithm that runs using unsorted arrays. – Drone Brain Apr 04 '13 at 16:38
1

Ruby implementation

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
SuperNova
  • 25,512
  • 7
  • 93
  • 64
0

Here is a solution in 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
vasilenicusor
  • 2,023
  • 1
  • 21
  • 37
0

I was asked this same question during an interview, and this is the scheme I had in mind. There's an improvement left to do, to permit negative numbers, but it would only be necessary to modify the indexes. Space-wise ain't good, but I believe running time here would be O(N)+O(N)+O(subset of N) -> O(N). I may be wrong.

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

Critics are welcomed.

0

In java, this is depends on max number in array. it returns an int[] having the indexes of two elements. it is 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;
}
Bruce Zu
  • 507
  • 6
  • 17
0

First you should find reverse array => sum minus actual array then check whether any element from these new array exist in the actual array.

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);
Fred Gandt
  • 4,217
  • 2
  • 33
  • 41
Coşkun Deniz
  • 2,049
  • 1
  • 14
  • 11
0

Naïve double loop printout with O(n x n) performance can be improved to linear O(n) performance using O(n) memory for Hash Table as follows:

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();
}
Thomas Fritsch
  • 9,639
  • 33
  • 37
  • 49
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)

The solution in python

0
        //C# - https://dotnetfiddle.net/6fMtze
        private static void PairNumbers(int[] numbers, int? _min, int sum)
        {
            if (numbers.Count() <= 1) return;

            int min = numbers[0];
            int max = numbers[^1];

            if (_min == max) return;

            var numberList = numbers.ToList();
            if (min + max >= sum)
            {
                if (min != _min)
                {
                    Console.WriteLine("{" + min + ", " + max + "}");
                }
                numberList.RemoveAt(numberList.Count - 1);
                RemoveDuplicates(numberList, min, max);
            }
            else
            {
                numberList.RemoveAt(0);
            }

            PairNumbers(numberList.ToArray(), min, sum);
        }
-1

Not guaranteed to be possible; how is the given sum selected?

Example: unsorted array of integers

2, 6, 4, 8, 12, 10

Given sum:

7

??

user732933
  • 278
  • 1
  • 5