6 Stimmen

Erstellen Sie eine Duplikatkopie der verketteten Liste in O(n) Zeit

Eine Verknüpfungsliste wird mit zwei Zeigern angegeben, der erste zeigt auf den nächsten Knoten und der andere zeigt auf einen zufälligen Zeiger. Der Zufallszeiger zeigt auf einen beliebigen Knoten der Verknüpfungsliste. Schreiben Sie ein vollständiges Programm zum Erstellen einer Kopie der Verknüpfungsliste (c, c ++, c#), ohne die ursprüngliche Liste zu ändern und in O(n).

Ich wurde in einem der Interviews nach dieser Frage gefragt und konnte die Lösung nicht herausfinden. Hilfe würde geschätzt werden.

1 Stimmen

Der erste Zeiger zeigt auf welchen Knoten? Der "nächste Knoten" ergibt aus dem Kontext, den ich lese, keinen Sinn.

12 Stimmen

Fehlt mir etwas? Warum würdest du nicht einfach die Liste genau einmal durchlaufen, indem du den Zeiger auf den nächsten Knoten verwendest und die Liste auf diese Weise kopierst?

1 Stimmen

Ich vermute, dass das Ziel der Interviewfrage war, den Kandidaten dazu zu bringen, dem Interviewer Fragen zu stellen, wie zum Beispiel nach dem Zweck der Zeiger - denn sonst scheint die Frage zu einfach für ein Interview zu sein (es sei denn, es handelte sich um ein Telefon-Vorstellungsgespräch).

17voto

Jerry Coffin Punkte 452852

Das Kopieren einer normalen verketteten Liste in linearer Zeit ist offensichtlich trivial. Der einzige Teil, der dies interessant macht, ist der "zufällige" Zeiger. Vermutlich meinen Sie mit "zufällig" wirklich, dass er auf einen anderen zufällig gewählten Knoten in derselben verketteten Liste zeigt. Vermutlich soll die Kopie der verketteten Liste genau dieselbe Struktur wiederherstellen - d.h., die 'next'-Zeiger erstellen eine lineare Liste, und die anderen Zeiger verweisen auf dieselben relativen Knoten (zum Beispiel, wenn der zufällige Zeiger im ersten Knoten der Originalliste auf den fünften Knoten in der Originalliste zeigte, dann würde der zufällige Zeiger in der Duplikatliste auch auf den fünften Knoten der Duplikatliste zeigen.

Das in N2-Zeit zu machen, ist ziemlich einfach. Zunächst wird die Liste normal dupliziert, wobei der zufällige Zeiger ignoriert wird. Dann gehen Sie durch die Originalliste einen Knoten nach dem anderen und gehen für jeden Knoten erneut durch die Liste, um herauszufinden, auf welchen Knoten der Liste der zufällige Zeiger verwies (d.h., wie viele Knoten Sie über die next-Zeiger durchlaufen müssen, um einen next-Zeiger zu finden, der dieselbe Adresse wie der random-Zeiger des aktuellen Knotens enthält. Dann durchlaufen Sie die Duplikatliste und stellen das umgekehrt dar - finden Sie die Adresse des Nten Knotens und setzen Sie das in den zufälligen Zeiger des aktuellen Knotens.

Der Grund, warum dies O(N2) ist, sind hauptsächlich diese linearen Suchen nach den richtigen Knoten. Um O(N) zu erreichen, müssen diese Suchen mit konstanter Komplexität anstelle von linearer Komplexität durchgeführt werden.

Der offensichtliche Weg, dies zu tun, wäre der Aufbau einer Hashtabelle, die die Adresse jedes Knotens in der Originalliste auf die Position dieses Knotens in der Liste abbildet. Dann können wir ein Array erstellen, das die Adressen der Knoten in der neuen Liste enthält.

Mit diesen ist es ziemlich einfach, die zufälligen Zeiger zu reparieren. Zuerst gehen wir durch die Originalliste über die next-Zeiger, duplizieren die Knoten und bauen unsere neue Liste über die next-Zeiger verbunden auf, lassen aber die zufälligen Zeiger unverändert. Dabei fügen wir die Adresse und Position jedes Knotens in die Hashtabelle ein und die Adresse jedes Knotens in der neuen Liste in unser Array.

Wenn wir damit fertig sind, gehen wir die alte Liste und die neue Liste im Gleichschritt durch. Für jeden Knoten in der alten Liste sehen wir auf die Adresse im zufälligen Zeiger dieses Knotens. Wir schlagen die mit dieser Adresse verbundene Position in unserer Hashtabelle nach, erhalten dann die Adresse des Knotens in der neuen Liste an dieser Position und setzen sie in den zufälligen Zeiger des aktuellen Knotens in der neuen Liste. Dann gehen wir zum nächsten Knoten in der alten und neuen Liste über.

Wenn wir fertig sind, werfen/wir zerstören sowohl die Hashtabelle als auch das Array, da unsere neue Liste nun die Struktur der alten dupliziert und wir die zusätzlichen Daten nicht mehr benötigen.

0 Stimmen

Guter Punkt, ohne weitere Aufmerksamkeit kann man nicht einfach den 1. Knoten kopieren, da er auf einen Knoten verweist, der noch nicht existiert.

2 Stimmen

+1 für das Verständnis, dass der Ersteller gemeint hat, dass es sich bei "A link list is given with two pointer ..." um "Eine verkettete Liste mit Knoten, die zwei Zeiger enthalten ..." handelt.

0 Stimmen

Perfekte Antwort. Dadurch müssen Sie nur zweimal durchlaufen, anstatt O(n^2). +1

8voto

divyanshm Punkte 6302

Übersicht des Ansatzes
Wir werden nicht versuchen, von Anfang an eine neue verkettete Liste zu erstellen. Duplizieren Sie zuerst die Elemente in der Original verketteten Liste und teilen Sie dann die Liste in 2 identische Listen auf.

Die Details
Schritt 1 : Durchlaufen Sie die verkettete Liste mit den nächsten Zeigern und erstellen Sie eine Kopie jedes Knotens.

original :: A -> B -> C -> D -> E ->null

updated :: A -> A' -> B -> B' -> C -> C' -> D -> D' ->E -> E' -> null

Dies kann leicht in einer einzigen Schleife erledigt werden.

Schritt 2 : Durchlaufen Sie die Liste erneut und beheben Sie die zufälligen Zeiger wie folgt

Node *current= start;
Node *newListNode, *random;
while (current!= null) {
    // Wenn der aktuelle Knoten einen zufälligen Zeiger hat, sagen wir current = A, A.random = D;
    if ( (*current)->random != null ) {
        // newListNode wird auf A' zeigen
        newListNode = (*current)->next;
        random = (*current)->random;
        random = (*random)->next;
        // Lassen Sie A' auf D's nächsten zeigen, der D' ist
        (*newListNode)->random = random;
    }
    // Hier A'.random = D'
    // Aktuelle geht zum nächsten Knoten in der Original liste => B , und nicht A'
    current= (*newListNode)->next;
}

Schritt 3 :: Teilen Sie die Liste in 2 Listen auf. Sie müssen nur die nächsten Zeiger hier korrigieren.

Original :: A -> A' -> B -> B' -> C -> C' -> D -> D' ->E -> E' -> null
Neu :: A -> B -> C -> D -> E ->null
       A' -> B' -> C' -> D' -> E' ->null

Node *start1 = head;
Node *start2 = (*head) ->next      // Bitte überprüfen Sie die Grenzbedingungen selbst,
                                   // Ich gehe davon aus, dass die Eingabe eine gültige Liste war
Node *next, *traverse = start2;
while (traverse != null) {
    next = (*traverse)->next;
    if (next == null) {
        (*traverse)->next = null;
        break;
    }
    (*traverse)->next = (*next)->next;
    traverse = next;
}

Auf diese Weise haben Sie eine Kopie der Original liste in O(n) Zeit erstellt. Sie durchlaufen die Liste 3 Mal, was immer noch der Ordnung von n entspricht.

7voto

molbdnilo Punkte 61369

Klärungseditierung:

Das folgende funktioniert nur, wenn die "zufälligen" Zeiger eindeutig sind, wie von BowieOwens angemerkt.
Ich lasse die Antwort nur für die allgemeine Idee stehen, aber bitte nicht hochwerten, da sie definitiv falsch ist.


Wenn ich mich nicht vollkommen irre, kannst du dies ohne Verwendung von zusätzlichem Speicher tun.

Wenn du die alte Liste kopierst, speichere den random-Zeiger des alten Knotens im random-Zeiger des neuen Knotens.
Setze dann den random-Zeiger des alten Knotens auf den neuen Knoten.

Dies wird dir eine "Zig-Zag"-Struktur zwischen der alten Liste und der neuen Liste geben.

Pseudocode:

Node* alter_knoten = ;
Node * neuer_knoten = new Node;
neuer_knoten->random = alter_knoten->random;
alter_knoten->random = neuer_knoten;

Sobald du die alte Liste auf diese Weise kopiert hast, fängst du von vorne an, ersetzt aber die random-Zeiger so, dass die Zeiger in der alten Liste wiederhergestellt werden, während die random-Zeiger in der neuen Liste auf die entsprechenden neuen Knoten gesetzt werden:

Node* alter_zufall = alter_knoten->random->random; // alte Liste -> neue Liste -> alte Liste
Node* neuer_zufall = neuer_knoten->random->random; // neue Liste -> alte Liste -> neue Liste
alter_knoten->random = alter_zufall;
neuer_knoten->random = neuer_zufall;

Es sieht auf dem Papier viel besser aus, aber ich fürchte, meine ASCII-Kunstfähigkeiten reichen nicht aus.

Dies verändert die ursprüngliche Liste, aber sie wird in den ursprünglichen Zustand zurückversetzt.
Ob das zulässig ist, hängt wohl vom Interview ab, denke ich.

1 Stimmen

Bricht das zusammen, wenn zwei Knoten in der Liste auf denselben zufälligen Knoten verweisen?

1 Stimmen

+1: Ich bin mir nicht sicher, ob es genau eine Antwort auf die Frage ist, aber es ist auf jeden Fall eine ziemlich coole Technik.

1 Stimmen

@BowieOwens: Meinst du wie auf den Bildern, die ich gepostet habe? Ich glaube nicht.

0voto

davidbuzatto Punkte 8917

Wie Sie wissen sollten, bedeutet O(n) linear. Wenn Sie eine Kopie einer linearen Datenstruktur benötigen, werden Sie damit keine Probleme haben, da Sie einfach über die Knoten der ursprünglichen Liste iterieren müssen und für jeden besuchten Knoten einen neuen Knoten für die neue Liste erstellen werden. Ich denke, die Frage ist nicht gut formuliert, weil Sie einige Aspekte kennen müssen, um den Job zu erledigen wie: handelt es sich um eine zirkuläre Implementierung? Was ist der "nächste" Knoten? Der nächste Knoten eines Knotens oder der erste Knoten der Liste? Wie endet die Liste?

-1voto

hqt Punkte 28296

Vielleicht ist diese Frage ein Trick, um zu testen, wie du auf eigenartige Fragen antwortest, weil es eigentlich sehr einfach ist:

1) Iteriere durch jeden Knoten

2) Erstelle einen neuen Knoten für eine andere verkettete Liste und kopiere den Wert.

Die Kosten sind immer O(n): weil du nur einmal die gesamte verkettete Liste durchläufst!!!

Oder du hast etwas falsch verstanden bei seiner Frage.

0 Stimmen

Es gibt auch einen Zufallszeiger für jeden Knoten. In einem Durchgang können Sie keine exakte Kopie erstellen (der Wert der Zufallszeiger in der Duplikatliste sollte derselbe sein wie die Zufallszeiger in der Originalliste).

0 Stimmen

@DownVoter, bitte erkläre mir, warum du downvotest. Gibt es Probleme?

0 Stimmen

@hqt Vermutlich deshalb, weil es davon ausgeht, dass die Daten einfach nur Daten sind, wenn es sich tatsächlich um eine absolute Referenz zu einem Element der verketteten Liste handelt, die in eine absolute Referenz auf das neue Element der verketteten Liste umgewandelt werden sollte.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X