Ich lese Cracking das Coding Interview, vierte Ausgabe: 150 Programmierung Interview Fragen und Lösungen und ich ' Ich versuche die folgende Frage zu lösen:
2.1 Schreiben Sie Code, um Duplikate aus einer unsortierten verketteten Liste zu entfernen. FOLGEN UP: Wie würdest du dieses Problem lösen, wenn ein temporärer Puffer ist nicht erlaubt?
Ich löse es in C #, also habe ich meine eigene Node
Klasse erstellt:
Meine Lösung besteht darin, die Liste zu durchlaufen und dann für jeden Knoten den Rest der Liste zu durchlaufen und alle Duplikate zu entfernen (beachten Sie, dass ich dies nicht wirklich kompiliert oder getestet habe, wie im Buch beschrieben):
%Vor%Hier ist die Lösung aus dem Buch (der Autor schrieb es in Java):
%Vor%Ohne einen Puffer können wir mit iterieren zwei Zeiger: "current" macht einen normalen Iteration, während "Läufer" iteriert durch alle vorherigen Knoten zu prüfen Duplikate. Runner wird nur eine dup pro sehen Knoten, denn wenn es mehrere gab Duplikate wären sie gewesen schon entfernt.
Also sucht meine Lösung immer nach Duplikaten für den aktuellen Knoten bis zum Ende, während ihre Lösung nach Duplikaten vom Kopf zum aktuellen Knoten sucht. Ich habe das Gefühl, dass beide Lösungen Leistungsprobleme haben werden, abhängig davon, wie viele Duplikate in der Liste enthalten sind und wie sie verteilt sind (Dichte und Position). Aber im Allgemeinen: Ist meine Antwort fast so gut wie die im Buch oder ist sie wesentlich schlechter?
Wenn Sie einer Person einen Fisch geben, essen sie einen Tag lang. Wenn Sie einer Person beibringen zu fischen ...
Meine Maßnahmen für die Qualität einer Implementierung sind:
Wie für Ihre Implementierung:
Es gibt keinen großen Unterschied. Wenn ich meine Mathematik richtig gemacht habe, ist Ihr Durchschnitt durchschnittlich N / 16 langsamer als die Autoren, aber es gibt Fälle, wo Ihre Implementierung schneller sein wird.
Bearbeiten:
Ich rufe Ihre Implementierung Y und das A des Autors
anBeide vorgeschlagenen Lösungen haben O (N ^ 2) als schlechtesten Fall und beide haben den besten Fall von O (N), wenn alle Elemente den gleichen Wert haben.
BEARBEITEN: Dies ist eine vollständige Neuschreibung. Inspiriert durch das Debat in den Kommentaren versuchte ich den durchschnittlichen Fall für zufällige N Zufallszahlen zu finden. Das ist eine Sequenz mit zufälliger Größe und zufälliger Verteilung. Was wäre der durchschnittliche Fall?
Y wird immer U-mal laufen, wobei U die Anzahl der eindeutigen Zahlen ist. Für jede Iteration werden N-X-Vergleiche durchgeführt, wobei X die Anzahl der Elemente ist, die vor der Iteration (+1) entfernt wurden. Das erste Mal, dass kein Element entfernt wurde und im Durchschnitt bei der zweiten Iteration wurde N / U entfernt.
Das heißt, im Durchschnitt wird ½N iteriert. Wir können die durchschnittlichen Kosten als ausdrücken U * ½N. Der Durchschnittswert U kann basierend auf N und 0 ausgedrückt werden
Das Ausdrücken von A wird schwieriger. Nehmen wir an, wir verwenden I Iterationen, bevor wir alle eindeutigen Werte gefunden haben. Danach wird zwischen 1 und U Vergleichen laufen (im Durchschnitt ist das U / ") und das wird N-I mal machen.
I * c + U / 2 (N-I)
aber was ist die durchschnittliche Anzahl der Vergleiche (c) wir für die ersten I Iterationen laufen. Im Durchschnitt müssen wir die Hälfte der bereits besuchten Elemente vergleichen, und im Durchschnitt haben wir I / 2-Elemente besucht, dh. c = I / 4
I / 4 + U / 2 (N-I).
Ich kann ausgedrückt werden als N. Im Durchschnitt müssen wir die Hälfte von N aufsuchen, um die eindeutigen Werte zu finden, so dass I = N / 2 ergibt einen Durchschnitt von
(I ^ 2) / 4 + U / 2 (N-I), die auf (3 * N ^ 2) / 16 reduziert werden kann.
Das ist natürlich, wenn meine Schätzung der Durchschnittswerte richtig ist. Das ist im Durchschnitt für jede mögliche Sequenz A hat N / 16 weniger Vergleiche als Y, aber es gibt Fälle, in denen Y schneller als A ist. Ich würde also sagen, dass sie im Vergleich zur Anzahl der Vergleiche gleich sind
Wie wäre es mit einer HashMap? Auf diese Weise dauert es O (n) Zeit und O (n) Raum. Ich werde psuedocode schreiben.
%Vor%Wir nehmen natürlich an, dass HashMap O (1) lesen und schreiben kann.
Eine andere Lösung besteht darin, einen Mergesort zu verwenden und das Duplikat vom Anfang bis zum Ende der Liste zu entfernen. Dies erfordert O (n log n)
Mergesort ist O (n log n) Entfernen von Duplikaten aus einer sortierten Liste ist O (n). weißt du, warum? daher benötigt die gesamte Operation O (n log n)
Heapsort ist ein an Ort und Stelle sortieren. Sie können die Funktion "siftUp" oder "siftDown" ändern, um das Element einfach zu entfernen, wenn es auf ein gleichwertiges Elternelement trifft. Dies wäre O (n log n)
%Vor%Ihre Lösung ist genauso gut wie die des Autors, nur hat sie einen Fehler in der Implementierung :) Versuchen Sie, sie auf einer Liste von zwei Knoten mit gleichen Daten zu verfolgen.
Ihr Ansatz ist einfach nur ein Spiegelbild des Buches! Du gehst vorwärts, das Buch geht rückwärts. Es gibt keinen Unterschied, da Sie beide alle Elemente scannen. Und, ja, da kein Puffer zulässig ist, gibt es Leistungsprobleme. In der Regel müssen Sie sich nicht mit solchen überlasteten Fragen auseinandersetzen, wenn diese Fragen nicht berücksichtigt werden.
Interviewfragen werden gemacht, um deine offene Mentalität zu testen. Ich habe Zweifel an Marks Antwort: Es ist definitiv die beste Lösung in realen Beispielen, aber selbst wenn diese Algorithmen konstanten Speicherplatz verwenden, muss die Einschränkung, dass kein temporärer Puffer erlaubt ist, respektiert werden.
>Ansonsten würde ich annehmen, dass das Buch einen solchen Ansatz angenommen hätte. Mark, bitte vergib mir, dass ich gegen dich kritisiert habe.
Wie auch immer, nur um tiefer in die Sache einzusteigen, benötigen Ihre und der Ansatz des Buches Theta(n^2)
time, während Marks Ansatz Theta(n logn) + Theta(n)
time benötigt, was in Theta(n logn)
resultiert. Warum Theta
? Da compare-swap-Algorithmen auch Omega(n logn)
sind, denken Sie daran!
Erprobt das gleiche in cpp. Bitte lassen Sie mich Ihre Kommentare dazu wissen.
// ConsoleApplication2.cpp: Definiert den Einstiegspunkt für die Konsolenanwendung. //
%Vor%Tags und Links java c# linked-list