Est-il un algorithme efficace d'autres que la force brutale de recherche pour trouver trois nombres entiers?
Yep; nous pouvons le résoudre en O(n2) temps! Tout d'abord, considérez votre problème P
peut être formulée de manière équivalente, de manière un peu différente, ce qui élimine la nécessité d'une "valeur cible":
problème d'origine P
: étant Donné un tableau A
de n
des entiers et une valeur cible S
, existe-t-il une 3-tuple de A
qui sommes à l' S
?
modifié problème P'
: étant Donné un tableau A
de n
des entiers, existe-t-il une 3-tuple de A
qui sommes à zéro?
Notez que vous pouvez aller à partir de cette version du problème, P'
de P
en soustrayant votre S/3 de chaque élément en A
, mais maintenant, vous n'avez pas besoin de la valeur cible plus.
Clairement, si on se contente de tester tous les possibles 3-n-uplets, nous aimerions résoudre le problème en O(n3)- c'est la force brute de base. Est-il possible de faire mieux? Si nous choisissons le tuples dans un peu plus intelligemment?
Tout d'abord, nous investir un peu de temps pour trier le tableau, qui nous coûte une pénalité initiale de O(n log n). Maintenant on exécute 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
, j
, et k
, à différents points dans le tableau. i
commence au début et lentement fait son chemin à la fin. k
points au dernier élément. j
de points à l'endroit où i
a commencé à. Nous itérative essayer de faire la somme des éléments à leurs indices respectifs, et chaque fois que l'un des cas suivants:
- La somme est exactement ça! Nous avons trouvé la réponse.
- La somme était trop petite. Déplacer
j
plus près de la fin pour sélectionner le prochain plus grand nombre.
- La somme était trop grand. Déplacer
k
plus près du début de sélectionner le prochain plus petit nombre.
Pour chaque i
, les pointeurs de j
et k
va progressivement se rapprocher les uns des autres. Finalement, ils vont passer les uns des autres, et à ce moment, nous n'avons pas besoin d'essayer quoi que ce soit, i
, puisque nous serions en additionnant les mêmes éléments, juste dans un ordre différent. Après ce point, nous essayons le prochain i
et répétez.
Finalement, nous allons soit épuiser les possibilités, ou nous allons trouver la solution. Vous pouvez voir que ce est O(n2) puisque l'on execute la boucle externe O(n) fois et on exécute la boucle interne O(n) fois. Il est possible de le faire sous-quadratiquement si vous avez vraiment de fantaisie, en représentant chaque entier comme un vecteur de bits et d'effectuer une transformée de Fourier rapide, mais c'est au-delà de la portée de cette réponse.
Remarque: Parce que c'est une question d'entrevue, j'ai triché un peu ici: cet algorithme permet la sélection de la même élément plusieurs fois. Qui est, (-1, -1, 2), serait une solution valide, comme le ferait (0, 0, 0). Elle aussi trouve que l' exacte des réponses, pas la réponse la plus exacte, comme le titre le mentionne. Comme exercice pour le lecteur, je vais vous le trouver comment le faire fonctionner avec des éléments distincts seulement (mais c'est un très simple changement) et des réponses exactes (qui est aussi un simple changement).