Existe-t-il un algorithme efficace autre que la recherche par force brute pour trouver les trois entiers?
Oui; nous pouvons résoudre cela en temps O (n 2 )! Tout d'abord, considérez que votre problème Ppeut être formulé de manière équivalente d'une manière légèrement différente qui élimine le besoin d'une «valeur cible»:
problème d'origine P: étant donné un tableau Ad' nentiers et une valeur cible S, existe-t-il un 3-tuple de Acette somme à S?
problème modifié P': étant donné un tableau Ad' nentiers, existe-t-il un 3-tuple de Acette somme à zéro?
Notez que vous pouvez passer de cette version du problème P'de Pen soustrayant votre S / 3 de chaque élément A, 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:
for (i in 1..n-2) {
j = i+1 // Start right after i.
k = n // Start at the end of the array.
while (k >= j) {
// We got a match! All done.
if (A[i] + A[j] + A[k] == 0) return (A[i], A[j], A[k])
// We didn't match. Let's try to get a little closer:
// If the sum was too big, decrement k.
// If the sum was too small, increment j.
(A[i] + A[j] + A[k] > 0) ? k-- : j++
}
// When the while-loop finishes, j and k have passed each other and there's
// no more useful combinations that we can try with this i.
}
Cet algorithme fonctionne en plaçant trois pointeurs, i, jet kà divers points du tableau. icommence par le début et avance lentement jusqu'à la fin. kpointe vers le tout dernier élément. jindique où ia 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:
- La somme est exactement la bonne! Nous avons trouvé la réponse.
- La somme était trop petite. Rapprochez-vous
jde la fin pour sélectionner le prochain plus grand nombre.
- La somme était trop importante. Rapprochez-
kvous du début pour sélectionner le plus petit nombre suivant.
Pour chacun i, les pointeurs de jet kse rapprochent progressivement les uns des autres. Finalement, ils se croiseront, et à ce stade, nous n'avons pas besoin d'essayer autre chose pour cela i, puisque nous additionnerions les mêmes éléments, juste dans un ordre différent. Après ce point, nous essayons le suivant iet 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).