Bei einem gegebenen Array von ganzen Zahlen A 1 , A 2 , ..., A n , einschließlich Negativen und Positiven, und einer weiteren ganzen Zahl S. Nun müssen wir drei verschiedene ganze Zahlen im Array finden, deren Summe der gegebenen ganzen Zahl S am nächsten kommt Wenn es mehr als eine Lösung gibt, ist eine davon in Ordnung.
Sie können davon ausgehen, dass alle Ganzzahlen im Bereich int32_t liegen und bei der Berechnung der Summe kein arithmetischer Überlauf auftritt. S ist nichts Besonderes als eine zufällig ausgewählte Zahl.
Gibt es einen anderen effizienten Algorithmus als die Brute-Force-Suche, um die drei ganzen Zahlen zu finden?
Antworten:
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:Beachten Sie, dass Sie von dieser Version des Problems gehen kann
P'
von derP
durch Subtrahieren Ihre S / 3 von jedem Element inA
, 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:
Dieser Algorithmus funktioniert durch drei Zeiger platzieren,
i
,j
, undk
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, woi
bei begonnen hat. Wir versuchen iterativ, die Elemente an ihren jeweiligen Indizes zu summieren, und jedes Mal passiert eines der folgenden Ereignisse:j
näher an das Ende heran, um die nächstgrößere Zahl auszuwählen.k
näher an den Anfang, um die nächstkleinere Zahl auszuwählen.Für jeden
i
nähern sich die Zeiger vonj
undk
allmählich an. Irgendwann werden sie sich überholen, und an diesem Punkt müssen wir nichts anderes dafür versucheni
, da wir die gleichen Elemente nur in einer anderen Reihenfolge summieren würden. Nach diesem Punkt versuchen wir den nächsteni
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.
quelle
Dies ist sicherlich eine bessere Lösung, da es leichter zu lesen und daher weniger fehleranfällig ist. Das einzige Problem ist, dass wir einige Codezeilen hinzufügen müssen, um die Mehrfachauswahl eines Elements zu vermeiden.
Eine weitere O (n ^ 2) -Lösung (unter Verwendung eines Hash-Sets).
quelle
s2
könnte ein bereits ausgewähltes Element sein. Wenn das Array beispielsweise ist0,1,2
undK
ist2
, sollte es keine Antwort geben. Ich denke, Ihr Algorithmus wird ausgegeben,0,1,1
was offensichtlich falsch ist.John Feminellas Lösung hat einen Fehler.
An der Linie
Wir müssen prüfen, ob i, j, k alle verschieden sind. Andernfalls, wenn mein Zielelement ist
6
und wenn mein Eingabearray enthält{3,2,1,7,9,0,-4,6}
. Wenn ich die Tupel ausdrucke, die sich zu 6 summieren, würde ich auch0,0,6
als Ausgabe erhalten. Um dies zu vermeiden, müssen wir die Bedingung auf diese Weise ändern.quelle
Wie wäre es mit so etwas, das O (n ^ 2) ist?
Dies stellt fest, ob die Summe von 3 Elementen genau Ihrer Zahl entspricht. Wenn Sie am nächsten kommen möchten, können Sie es so ändern, dass es sich an das kleinste Delta erinnert (Differenz zwischen der Anzahl der aktuellen Tripletts) und am Ende das Triplett druckt, das dem kleinsten Delta entspricht.
quelle
Beachten Sie, dass wir ein sortiertes Array haben. Diese Lösung ähnelt Johns Lösung nur darin, dass sie nach der Summe sucht und nicht dasselbe Element wiederholt.
quelle
a[r] + a[l] + a[i] - sum
. Anprobierenarr = [-1, 2, 1, -4] sum = 1
.Hier ist der C ++ - Code:
quelle
Sehr einfache N ^ 2 * logN-Lösung: Sortieren Sie das Eingabearray, gehen Sie dann alle Paare A i , A j (N ^ 2-mal) durch und prüfen Sie für jedes Paar, ob (S - A i - A j ) im Array ist ( logN Zeit).
Eine andere O (S * N) -Lösung verwendet den klassischen Ansatz der dynamischen Programmierung .
Zusamenfassend:
Erstellen Sie ein 2D-Array V [4] [S + 1]. Füllen Sie es so aus, dass:
V [0] [0] = 1, V [0] [x] = 0;
V 1 [A i ] = 1 für jedes i, V 1 [x] = 0 für alle anderen x
V [2] [A i + A j ] = 1 für jedes i, j. V [2] [x] = 0 für alle anderen x
V [3] [Summe von 3 beliebigen Elementen] = 1.
Um es zu füllen, durchlaufen Sie A i , für jedes A i durchlaufen Sie das Array von rechts nach links.
quelle
Dies kann in O (n log (n)) wie folgt effizient gelöst werden. Ich gebe eine Lösung, die sagt, ob die Summe von drei Zahlen einer gegebenen Zahl entspricht.
quelle
leftIndex
oderrightIndex
wenn alle Elemente in der Mitte entweder streng kleiner oder größer als Ihre gewünschte Anzahl sind. Aber was ist mit dem Fall, als die binäre Suche irgendwo in der Mitte aufhörte? Sie müssten beide Zweige überprüfen (worightIndex--
undleftIndex++
). In Ihrer Lösung ignorieren Sie diese Situation einfach. Ich glaube jedoch nicht, dass es einen Weg gibt, dieses Problem zu lösen.Reduktion: Ich finde die @ John Feminella-Lösung O (n2) am elegantesten. Wir können immer noch das A [n] reduzieren, in dem nach Tupel gesucht werden soll. Indem Sie A [k] so beobachten, dass alle Elemente in A [0] - A [k] sind, wenn unser Sucharray groß und die Summe (n) wirklich klein ist.
Ein [0] ist Minimum: - Aufsteigendes sortiertes Array.
s = 2A [0] + A [k]: Mit s und A [] können wir A [k] mithilfe der binären Suche in log (n) -Zeit finden.
quelle
Hier ist das Programm in Java, das O (N ^ 2) ist.
quelle
Das Problem kann in O (n ^ 2) gelöst werden, indem das 2-Summen-Problem mit geringfügigen Modifikationen erweitert wird. A ist der Vektor, der Elemente enthält, und B ist die erforderliche Summe.
int Solution :: threeSumClosest (Vektor & A, int B) {
quelle
Hier ist der Python3-Code
quelle
Eine weitere Lösung, die frühzeitig überprüft und fehlschlägt:
Ich habe hier einige Komponententests hinzugefügt: GivenArrayReturnTrueIfThreeElementsSumZeroTest .
Wenn das Set zu viel Speicherplatz belegt, kann ich problemlos ein java.util.BitSet verwenden, das O (n / w) Speicherplatz verwendet .
quelle
Programm, um diese drei Elemente zu erhalten. Ich habe gerade zuerst das Array / die Liste sortiert und sie
minCloseness
basierend auf jedem Triplett aktualisiert .quelle
Ich habe das in n ^ 3 gemacht, mein Pseudocode ist unten;
// Eine HashMap mit dem Schlüssel als Ganzzahl und dem Wert als ArrayList erstellen. // Die Liste mit einer for-Schleife durchlaufen, wobei jeder Wert in der Liste ab dem nächsten Wert erneut iteriert wird.
// Wenn die Summe von arr [i] und arr [j] kleiner als die gewünschte Summe ist, besteht die Möglichkeit, eine dritte Ziffer zu finden, also machen Sie eine andere for-Schleife
// In diesem Fall suchen wir jetzt nach dem dritten Wert. Wenn die Summe von arr [i] und arr [j] und arr [k] die gewünschte Summe ist, fügen Sie diese zur HashMap hinzu, indem Sie arr [i] zum Schlüssel machen und dann arr [j] und arr [k] hinzufügen die ArrayList im Wert dieses Schlüssels
Danach haben Sie jetzt ein Wörterbuch, in dem alle Einträge die drei Werte darstellen, die zur gewünschten Summe addiert werden. Extrahieren Sie alle diese Einträge mit HashMap-Funktionen. Das hat perfekt funktioniert.
quelle