Gibt es einen anderen effizienten Algorithmus als die Brute-Force-Suche, um die drei ganzen Zahlen zu finden?
Ja; wir können dies in O (n 2 ) Zeit lösen ! Bedenken Sie zunächst, dass Ihr Problem P
auf eine etwas andere Weise äquivalent formuliert werden kann, sodass kein "Zielwert" erforderlich ist:
ursprüngliches Problem P
: Gibt es bei einem Array A
von n
ganzen Zahlen und einem Zielwert S
ein 3-Tupel von A
diesen Summen bis S
?
modifiziertes Problem P'
: Gibt es bei einem Array A
von n
ganzen Zahlen ein 3-Tupel von A
dieser Summe bis Null?
Beachten Sie, dass Sie von dieser Version des Problems gehen kann P'
von der P
durch Subtrahieren Ihre S / 3 von jedem Element in A
, aber jetzt müssen Sie nicht den Zielwert mehr benötigen.
Wenn wir einfach alle möglichen 3-Tupel testen, lösen wir das Problem in O (n 3 ) - das ist die Brute-Force-Basislinie. Kann man es besser machen? Was ist, wenn wir die Tupel etwas intelligenter auswählen?
Zuerst investieren wir etwas Zeit, um das Array zu sortieren, was uns eine anfängliche Strafe von O (n log n) kostet. Nun führen wir diesen Algorithmus aus:
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.
}
Dieser Algorithmus funktioniert durch drei Zeiger platzieren, i
, j
, und k
an verschiedenen Stellen in der Anordnung. i
beginnt am Anfang und arbeitet sich langsam bis zum Ende vor. k
zeigt auf das allerletzte Element. j
zeigt darauf, wo i
bei begonnen hat. Wir versuchen iterativ, die Elemente an ihren jeweiligen Indizes zu summieren, und jedes Mal passiert eines der folgenden Ereignisse:
- Die Summe ist genau richtig! Wir haben die Antwort gefunden.
- Die Summe war zu klein. Gehen Sie
j
näher an das Ende heran, um die nächstgrößere Zahl auszuwählen.
- Die Summe war zu groß. Gehen Sie
k
näher an den Anfang, um die nächstkleinere Zahl auszuwählen.
Für jeden i
nähern sich die Zeiger von j
und k
allmählich an. Irgendwann werden sie sich überholen, und an diesem Punkt müssen wir nichts anderes dafür versuchen i
, da wir die gleichen Elemente nur in einer anderen Reihenfolge summieren würden. Nach diesem Punkt versuchen wir den nächsten i
und wiederholen.
Schließlich werden wir entweder die nützlichen Möglichkeiten ausschöpfen oder die Lösung finden. Sie können sehen, dass dies O (n 2 ) ist, da wir die äußere Schleife O (n) mal ausführen und die innere Schleife O (n) mal ausführen. Es ist möglich, dies subquadratisch zu tun, wenn Sie wirklich Lust haben, indem Sie jede ganze Zahl als Bitvektor darstellen und eine schnelle Fourier-Transformation durchführen, aber das geht über den Rahmen dieser Antwort hinaus.
Hinweis: Da es sich um eine Interviewfrage handelt, habe ich hier ein wenig geschummelt: Dieser Algorithmus ermöglicht die mehrfache Auswahl desselben Elements. Das heißt, (-1, -1, 2) wäre eine gültige Lösung, ebenso wie (0, 0, 0). Es werden auch nur die genauen Antworten gefunden, nicht die nächstgelegene Antwort, wie im Titel erwähnt. Als Übung für den Leser werde ich Sie herausfinden lassen, wie es nur mit bestimmten Elementen (aber es ist eine sehr einfache Änderung) und genauen Antworten (was auch eine einfache Änderung ist) funktioniert.