web-dev-qa-db-fra.com

trouver une paire de nombres dans un tableau qui s'ajoute à une somme donnée

Question: Etant donné un tableau d’entiers positifs non triés, est-il possible de trouver une paire d’entiers de ce tableau qui totalisent une somme donnée? 

Contraintes: Cela devrait être fait dans O(n) et sur place (sans stockage externe comme les tableaux, les tables de hachage) (vous pouvez utiliser des variables/pointeurs supplémentaires).

Si ce n'est pas possible, peut-il y avoir une preuve pour la même chose?

31
noMAD

Si vous avez un tableau trié, vous pouvez trouver une telle paire dans O(n) en déplaçant deux pointeurs vers le milieu.

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;

Le tri peut être effectué O(N) si vous avez une limite sur la taille des nombres (ou si le tableau est déjà trié en premier lieu). Même dans ce cas, un facteur de log n est vraiment petit et je ne veux pas prendre la peine de le réduire.

preuve:

S'il existe une solution (i*, j*), supposons, sans perte de généralité, que i atteigne i* avant que j atteigne j*. Puisque pour tout j' entre j* et j, nous savons que a[j'] > a[j*] nous pouvons extrapoler ce a[i] + a[j'] > a[i*] + a[j*] = target et, par conséquent, que toutes les étapes suivantes de l’algorithme entraîneront une diminution de j jusqu’à atteindre j* (ou une valeur égale) sans donner à i avancer et "rater" la solution.

L'interprétation dans l'autre sens est similaire.

53
hugomg

Une solution d'espace O(N) time et O(1) qui fonctionne sur un tableau trié:

Soit M la valeur que vous recherchez. Utilisez deux pointeurs, X et Y. Commencez X=0 au début et Y=N-1 à la fin. Calculez la somme sum = array[X] + array[Y]. Si sum > M, décrémentez Y, sinon incrémentez X. Si les pointeurs se croisent, alors aucune solution n'existe.

Vous pouvez trier sur place pour obtenir ceci pour un tableau général, mais je ne suis pas sûr qu'il existe une solution d'espace O(N) time et O(1) en général.

12
PengOne

AS @PengOne a mentionné que ce n’était pas possible dans l’ordre général des choses. Mais si vous faites des restrictions sur les données i/p.

  1. tous les éléments sont tous + ou tous - sinon, il faudrait connaître la plage (haute, basse) et y apporter des modifications.
  2. K, la somme de deux entiers est clairsemée par rapport aux éléments en général.
  3. C'est bien de détruire le tableau i/p A [N].

Étape 1: Déplacez tous les éléments inférieurs à SUM au début du tableau, comme dans N Passes, nous avons divisé le tableau en [0, K] & [K, N-1], de sorte que [0, K] contient des éléments <= SUM.

Étape 2: Puisque nous connaissons les limites (0 à SUM), nous pouvons utiliser le tri de base. 

Étape 3: Utilisez la recherche binaire sur A [K]. Une bonne chose est que si nous avons besoin de trouver un élément complémentaire, il suffit de regarder la moitié du tableau A [K]. donc dans A [k] nous itérons sur A [0 à K/2 + 1] nous devons faire une recherche binaire dans A [i à K].

Donc total appx. le temps est, N + K + K/2 lg (K) où K est le nombre d'éléments entre 0 et Sum en i/p A [N]

Remarque: si vous utilisez l'approche de @ PengOne, vous pouvez effectuer l'étape 3 en K. Le temps total serait donc de N + 2K, ce qui est certainement O(N) 

Nous n'utilisons pas de mémoire supplémentaire, mais détruisons le tableau i/p, qui n'est également pas mauvais, car il n'avait aucun ordre pour commencer.

3
d1val

Cela peut être possible si le tableau contient des nombres dont vous connaissez au préalable la limite supérieure. Utilisez ensuite la méthode de comptage ou de tri de base (o (n)) et utilisez l’algorithme suggéré par @PengOne.

Sinon Je ne peux pas penser à la solution O(n). Mais la solution O(nlgn) fonctionne de la manière suivante: -

Commencez par trier le tableau à l'aide du tri par fusion ou du tri rapide (pour in situ). Recherchez si sum - array_element est présent dans ce tableau trié . On peut utiliser la recherche binaire pour cela.

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

Tout d’abord, triez le tableau avec radix sort . Cela vous coûtera O (kN). Procédez ensuite comme le suggère @PengOne.

2
Miquel

Voici un algorithme O(N). Il repose sur un algorithme de suppression de doublons sur place O(N) , et sur l'existence d'une fonction de hachage correcte pour les ints de votre tableau.

Tout d'abord, supprimez tous les doublons du tableau.

Deuxièmement, parcourez le tableau et remplacez chaque nombre x par min (x, S-x), où S est la somme que vous souhaitez atteindre.

Troisièmement, recherchez s'il y a des doublons dans le tableau: si 'x' est dupliqué, alors 'x' et 'S-x' doivent être apparus dans le tableau d'origine et vous avez trouvé votre paire.

2
Paul Hankin

Ma solution en Java (Time Complexity O (n)), cela produira toutes les paires avec une somme donnée

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]));
        }
    }
}
}
2
Chetan Kole

Le site suivant donne une solution simple en utilisant hashset qui voit un numéro, puis cherche dans le hashset un nombre somme-courant donné http://www.dsalgo.com/UnsortedTwoSumToK.php

2
  1. Utilisez le tri par nombre pour trier le tableau O (n).
  2. prenons deux pointeurs on part du 0ème index du tableau, et un autre de la fin du tableau dit (n-1).

    exécuter la boucle jusqu'à basse <= haute

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

    Les étapes 2 à 10 prennent O(n) temps, et le tri compte prend O (n). La complexité temporelle totale sera donc O (n).

2
Jay

En javascript: Ce code, lorsque n est supérieur à, augmente le temps et le nombre d'itérations. Le nombre de tests effectués par le programme sera égal à ((n * (n/2) + n/2) où n est le nombre d'éléments. Le nombre total donné est ignoré dans if (arr [i] + arr [j ] === 0) où 0 pourrait être n'importe quel nombre donné.

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));        
1
EJISRHA

Voici une solution qui prend en compte les entrées en double. Il est écrit en javascript et fonctionne à l'aide de tableaux triés et non triés. La solution s'exécute dans le temps 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;
}

Commencez des deux côtés de la matrice et avancez lentement en calculant le nombre de fois où chaque nombre est trouvé. Une fois que vous avez atteint le point médian, tous les chiffres sont comptés et vous pouvez maintenant faire progresser les deux pointeurs en comptant les paires au fur et à mesure. 

Il ne compte que les paires mais peut être retravaillé

  • trouve les paires 
  • trouver des paires <x 
  • trouver des paires> x

Prendre plaisir!

1
dRoneBrain

Ruby implémentation

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
1
SuperNova

On m'a posé la même question lors d'une interview et c'est le stratagème auquel je pensais. Il reste une amélioration à faire pour permettre les nombres négatifs, mais il ne faudrait que modifier les index. En ce qui concerne l'espace, ce n'est pas bien, mais je pense que le temps de course ici serait O (N) + O (N) + O (sous-ensemble de N) -> O (N). J'ai peut-être tort. 

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

Les critiques sont les bienvenues.

0
Jorge Alcantara

En Java, cela dépend du nombre maximum dans un tableau. il retourne un int [] ayant les index de deux éléments .

  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;
}
0
Bruce Zu

Voici une solution en 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
0
vasilenicusor

Une impression naïve à double boucle avec des performances O (n x n) peut être améliorée en performances linéaires O(n) à l'aide de la mémoire O(n) pour la table de hachage comme suit:

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

La solution en python 

0
user1464878

Pas garanti d'être possible; comment la somme donnée est-elle sélectionnée?

Exemple: tableau d'entiers non trié

2, 6, 4, 8, 12, 10

Somme donnée:

7

??

0
user732933

Vous devez d’abord trouver le tableau inversé => somme moins le tableau réel Puis vérifier s’il existe un élément de ce nouveau tableau dans le tableau actuel. 

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);
0
Coşkun Deniz