Étant donné un tableau d'entiers, A 1 , A 2 , ..., A n , y compris les négatifs et les positifs, et un autre entier S.Nous devons maintenant trouver trois entiers différents dans le tableau, dont la somme est la plus proche de l'entier S donné S'il existe plus d'une solution, l'une d'entre elles est correcte.
Vous pouvez supposer que tous les entiers sont compris dans la plage int32_t, et aucun débordement arithmétique ne se produira lors du calcul de la somme. S n'a rien de spécial mais un nombre choisi au hasard.
Existe-t-il un algorithme efficace autre que la recherche par force brute pour trouver les trois entiers?
Réponses:
Oui; nous pouvons résoudre cela en temps O (n 2 )! Tout d'abord, considérez que votre problème
P
peut être formulé de manière équivalente d'une manière légèrement différente qui élimine le besoin d'une «valeur cible»:Notez que vous pouvez passer de cette version du problème
P'
deP
en soustrayant votre S / 3 de chaque élémentA
, mais maintenant vous n'avez pas besoin de la valeur cible plus.Clairement, si nous testons simplement tous les 3 tuples possibles, nous résoudrons le problème en O (n 3 ) - c'est la ligne de base de la force brute. Est-il possible de faire mieux? Et si nous choisissions les tuples d'une manière un peu plus intelligente?
Tout d'abord, nous investissons du temps pour trier le tableau, ce qui nous coûte une pénalité initiale de O (n log n). Maintenant, nous exécutons cet algorithme:
Cet algorithme fonctionne en plaçant trois pointeurs,
i
,j
etk
à divers points du tableau.i
commence par le début et avance lentement jusqu'à la fin.k
pointe vers le tout dernier élément.j
indique oùi
a commencé. Nous essayons itérativement de faire la somme des éléments à leurs indices respectifs, et à chaque fois l'un des événements suivants se produit:j
de la fin pour sélectionner le prochain plus grand nombre.k
vous du début pour sélectionner le plus petit nombre suivant.Pour chacun
i
, les pointeurs dej
etk
se rapprochent progressivement les uns des autres. Finalement, ils se croiseront, et à ce stade, nous n'avons pas besoin d'essayer autre chose pour celai
, puisque nous additionnerions les mêmes éléments, juste dans un ordre différent. Après ce point, nous essayons le suivanti
et répétons.Finalement, soit nous épuiserons les possibilités utiles, soit nous trouverons la solution. Vous pouvez voir que c'est O (n 2 ) puisque nous exécutons la boucle externe O (n) fois et nous exécutons la boucle interne O (n) fois. Il est possible de le faire de manière sous-quadratique si vous avez vraiment envie, en représentant chaque entier comme un vecteur de bits et en effectuant une transformation de Fourier rapide, mais cela dépasse le cadre de cette réponse.
Remarque: Comme il s'agit d'une question d'entretien, j'ai un peu triché ici: cet algorithme permet de sélectionner plusieurs fois le même élément. Autrement dit, (-1, -1, 2) serait une solution valide, comme le ferait (0, 0, 0). Il ne trouve également que les réponses exactes , pas la réponse la plus proche, comme l'indique le titre. En guise d'exercice pour le lecteur, je vais vous laisser comprendre comment le faire fonctionner avec des éléments distincts uniquement (mais c'est un changement très simple) et des réponses exactes (ce qui est également un changement simple).
la source
c'est certainement une meilleure solution car elle est plus facile à lire et donc moins sujette aux erreurs. Le seul problème est que nous devons ajouter quelques lignes de code pour éviter la sélection multiple d'un élément.
Une autre solution O (n ^ 2) (en utilisant un hashset).
la source
s2
peut être un élément déjà sélectionné. Par exemple, si le tableau est0,1,2
etK
est2
, il ne devrait pas y avoir de réponse. Je pense que votre algorithme produira0,1,1
ce qui est évidemment incorrect.La solution de John Feminella a un bug.
À la ligne
Nous devons vérifier si i, j, k sont tous distincts. Sinon, si mon élément cible est
6
et si mon tableau d'entrée contient{3,2,1,7,9,0,-4,6}
. Si j'imprime les tuples qui totalisent 6, alors j'obtiendrai également0,0,6
en sortie. Pour éviter cela, nous devons modifier la condition de cette manière.la source
Que diriez-vous de quelque chose comme ça, qui est O (n ^ 2)
Cela trouve si la somme de 3 éléments est exactement égale à votre nombre. Si vous voulez le plus proche, vous pouvez le modifier pour mémoriser le plus petit delta (différence entre votre nombre de triplet courant) et à la fin imprimer le triplet correspondant au plus petit delta.
la source
Notez que nous avons un tableau trié. Cette solution est similaire à la solution de John uniquement en ce qu'elle recherche la somme et ne répète pas le même élément.
la source
a[r] + a[l] + a[i] - sum
. Essayezarr = [-1, 2, 1, -4] sum = 1
.Voici le code C ++:
la source
Solution N ^ 2 * logN très simple: trier le tableau d'entrée, puis parcourir toutes les paires A i , A j (temps N ^ 2), et pour chaque paire vérifier si (S - A i - A j ) est dans le tableau ( heure logN).
Une autre solution O (S * N) utilise une approche de programmation dynamique classique .
En bref:
Créez un tableau 2D V [4] [S + 1]. Remplissez-le de telle manière que:
V [0] [0] = 1, V [0] [x] = 0;
V 1 [A i ] = 1 pour tout i, V 1 [x] = 0 pour tout autre x
V [2] [A i + A j ] = 1, pour tout i, j. V [2] [x] = 0 pour tous les autres x
V [3] [somme de 3 éléments quelconques] = 1.
Pour le remplir, parcourez A i , pour chaque A i, parcourez le tableau de droite à gauche.
la source
Cela peut être résolu efficacement en O (n log (n)) comme suit. Je donne une solution qui indique si la somme de trois nombres est égale à un nombre donné.
la source
leftIndex
ourightIndex
lorsque tous les éléments du milieu sont soit strictement plus petits ou plus grands que le nombre souhaité. Mais qu'en est-il du cas où la recherche binaire s'est arrêtée quelque part au milieu? Vous devrez vérifier les deux branches (oùrightIndex--
etleftIndex++
). Dans votre solution, vous ignorez simplement cette situation. Mais je ne pense pas qu'il existe un moyen de surmonter ce problème.Réduction: Je pense que la solution O (n2) de @John Feminella est la plus élégante. Nous pouvons encore réduire le A [n] dans lequel rechercher un tuple. En observant A [k] de telle sorte que tous les éléments soient dans A [0] - A [k], lorsque notre tableau de recherche est énorme et SUM (s) vraiment petit.
Un [0] est au minimum: - Tableau trié croissant.
s = 2A [0] + A [k]: Étant donné s et A [], nous pouvons trouver A [k] en utilisant une recherche binaire en log (n) temps.
la source
Voici le programme en java qui est O (N ^ 2)
la source
Le problème peut être résolu en O (n ^ 2) en étendant le problème à 2 sommes avec des modifications mineures: A est le vecteur contenant les éléments et B est la somme requise.
int Solution :: threeSumClosest (vector & A, int B) {
la source
Voici le code Python3
la source
Une autre solution qui vérifie et échoue tôt:
J'ai ajouté quelques tests unitaires ici: GivenArrayReturnTrueIfThreeElementsSumZeroTest .
Si l'ensemble utilise trop d'espace, je peux facilement utiliser un java.util.BitSet qui utilisera l' espace O (n / w) .
la source
Programme pour obtenir ces trois éléments. Je viens de trier le tableau / la liste en premier et les mettre à jour en
minCloseness
fonction de chaque triplet.la source
J'ai fait ça en n ^ 3, mon pseudocode est ci-dessous;
// Créer un hashMap avec la clé comme Integer et la valeur comme ArrayList // itérer dans la liste en utilisant une boucle for, pour chaque valeur de la liste, itérer à nouveau à partir de la valeur suivante;
// si la somme de arr [i] et arr [j] est inférieure à la somme souhaitée, alors il est possible de trouver un troisième chiffre alors faites-en une autre boucle for
// dans ce cas, nous recherchons maintenant la troisième valeur; si la somme de arr [i] et arr [j] et arr [k] est la somme souhaitée, ajoutez-les au HashMap en faisant de arr [i] la clé puis en ajoutant arr [j] et arr [k] dans le ArrayList dans la valeur de cette clé
après cela, vous avez maintenant un dictionnaire qui contient toutes les entrées représentant les trois valeurs s'ajoutant à la somme souhaitée. Extrayez toutes ces entrées à l'aide des fonctions HashMap. Cela a parfaitement fonctionné.
la source