Warum wird in der Mitte einer verknüpften Liste O (1) eingefügt?


104

Gemäß dem Wikipedia-Artikel über verknüpfte Listen wird das Einfügen in die Mitte einer verknüpften Liste als O (1) betrachtet. Ich würde denken, es wäre O (n). Müssen Sie nicht den Knoten suchen, der sich am Ende der Liste befinden könnte?

Berücksichtigt diese Analyse nicht das Auffinden der Knotenoperation (obwohl dies erforderlich ist) und nur das Einfügen selbst?

EDIT :

Verknüpfte Listen haben gegenüber Arrays mehrere Vorteile. Das Einfügen eines Elements an einem bestimmten Punkt einer Liste ist eine Operation mit konstanter Zeit, während das Einfügen in ein Array möglicherweise das Verschieben der Hälfte der Elemente oder mehr erfordert.

Die obige Aussage ist für mich etwas irreführend. Korrigieren Sie mich, wenn ich falsch liege, aber ich denke, die Schlussfolgerung sollte lauten:

Arrays:

  • Finden des Einfüge- / Löschpunktes O (1)
  • Einfügen / Löschen durchführen O (n)

Verknüpfte Listen:

  • Finden des Einfüge- / Löschpunktes O (n)
  • Einfügen / Löschen durchführen O (1)

Ich denke, das einzige Mal, dass Sie die Position nicht finden müssten, wäre, wenn Sie eine Art Zeiger darauf behalten würden (wie in einigen Fällen mit Kopf und Schwanz). Wir können also nicht einfach sagen, dass verknüpfte Listen immer Arrays für Einfüge- / Löschoptionen schlagen.

Antworten:


113

Sie haben Recht, der Artikel betrachtet "Indizierung" als separate Operation. Das Einfügen ist also selbst O (1), aber das Erreichen dieses mittleren Knotens ist O (n).


3
Was einen größeren Unterschied macht, wenn mehr als 1 Objekt an derselben Position
eingefügt wird

@ Anony-Mousse kannst du es ein bisschen mehr erklären? dh wir müssen die Einfügeposition nur einmal finden, wenn wir mehrere Objekte einfügen?
MyTitle

2
Es ist O (n) in der Größe der vorhandenen Liste, nicht die Anzahl der Einfügungen, die Sie dort vornehmen möchten.
Hat aufgehört - Anony-Mousse

27

Die Einfügung selbst ist O (1). Die Knotenfindung ist O (n).


24

Nein, wenn Sie sich entscheiden, dass Sie einfügen möchten, wird davon ausgegangen, dass Sie sich gerade in der Iteration der Liste befinden.

Operationen an verknüpften Listen werden häufig so ausgeführt, dass sie nicht wirklich als generische "Liste", sondern als Sammlung von Knoten behandelt werden. Stellen Sie sich den Knoten selbst als Iterator für Ihre Hauptschleife vor. Wenn Sie also durch die Liste stöbern, stellen Sie als Teil Ihrer Geschäftslogik fest, dass ein neuer Knoten hinzugefügt (oder ein alter gelöscht) werden muss, und Sie tun dies. Sie können 50 Knoten in einer einzigen Iteration hinzufügen, und jeder dieser Knoten ist nur O (1) die Zeit, um zwei benachbarte Knoten zu trennen und Ihren neuen einzufügen.

Bearbeiten: Mann, Sie geben einen zweiten Absatz ein und plötzlich, anstatt der Ersthelfer zu sein, sind Sie der 5., der dasselbe sagt wie die ersten 4!


1
Ha ja, das ist scheiße ... ich + 1 wäre deins, weil es sich lohnt zu erwähnen, dass die Komplexität beim Einfügen verknüpfter Listen im Zusammenhang damit betrachtet wird, bereits am gewünschten Zeiger zu sein.
Daniel Macias

6

Zum Vergleich mit einem Array, wie in diesem Diagramm dargestellt, ist es O (1), da Sie nicht alle Elemente nach dem neuen Knoten verschieben müssen.

Ja, sie gehen davon aus, dass Sie bereits den Zeiger auf diesen Knoten haben oder dass das Abrufen des Zeigers trivial ist. Mit anderen Worten, das Problem wird angegeben: " Wenn der Knoten bei X angegeben ist , welchen Code muss nach diesem Knoten eingefügt werden?" Sie können am Einfügepunkt beginnen.


5

Das Einfügen in eine verknüpfte Liste unterscheidet sich vom Durchlaufen. Sie finden das Element nicht, sondern setzen Zeiger zurück, um das Element dort abzulegen. Es spielt keine Rolle, ob es in der Nähe des vorderen Endes oder in der Nähe des Endes eingefügt wird. Beim Einfügen werden immer noch Zeiger neu zugewiesen. Es hängt natürlich davon ab, wie es implementiert wurde, aber das ist die Stärke von Listen - Sie können sie einfach einfügen. Beim Zugriff über den Index leuchtet ein Array. Für eine Liste ist es jedoch normalerweise O (n), um das n-te Element zu finden. Zumindest erinnere ich mich daran aus der Schule.


3

Weil es keine Schleife beinhaltet.

Das Einfügen ist wie folgt:

  • Element einfügen
  • Link zum vorherigen
  • Link zum nächsten
  • getan

Dies ist in jedem Fall eine konstante Zeit.

Folglich ist das Einfügen von n Elementen nacheinander O (n).


3

Berücksichtigt diese Analyse nicht das Auffinden der Knotenoperation (obwohl dies erforderlich ist) und nur das Einfügen selbst?

Du hast es. Beim Einfügen an einem bestimmten Punkt wird davon ausgegangen, dass Sie bereits einen Zeiger auf das Element halten, nach dem Sie einfügen möchten:

InsertItem(item * newItem, item * afterItem)


2

Nein, die Suche wird nicht berücksichtigt. Wenn Sie jedoch bereits einen Zeiger auf ein Element in der Mitte der Liste haben, ist das Einfügen an dieser Stelle O (1).

Wenn Sie danach suchen müssen, müssen Sie die Zeit für die Suche hinzufügen, die O (n) sein sollte.


0

Der Artikel befasst sich mit dem Vergleich von Arrays mit Listen. Das Finden der Einfügeposition für Arrays und Listen ist O (N), daher ignoriert der Artikel sie.


1
Wäre es nicht O (1), die Einfügemarke eines Arrays zu finden? Da Arrays im zusammenhängenden Speicher gespeichert sind, muss lediglich der Offset hinzugefügt werden.
Rob Sobers

@ vg1890 - Sie müssen zuerst den Offset finden.

0

O (1) hängt davon ab, dass Sie einen Artikel haben, in den Sie den neuen Artikel einfügen. (vorher oder nachher). Wenn Sie dies nicht tun, ist es O (n), da Sie diesen Gegenstand finden müssen.


0

Ich denke, es ist nur ein Fall von dem, was Sie für die O () - Notation zählen. Beim Einfügen der zu zählenden normalen Operation handelt es sich um Kopieroperationen. Bei einem Array müssen Sie beim Einfügen in die Mitte alles über den Speicherort kopieren. Bei einer verknüpften Liste werden zwei Zeiger gesetzt. Sie müssen den Ort finden, egal was eingefügt werden soll.


0

Wenn Sie die Referenz des Knotens haben, der nach der Operation eingefügt werden soll, ist O (1) für eine verknüpfte Liste.
Für ein Array ist es immer noch O (n), da Sie alle aufeinander folgenden Knoten verschieben müssen.


0

Die häufigsten Fälle sind wahrscheinlich das Einfügen am Anfang oder am Ende der Liste (und das Finden der Listenenden dauert möglicherweise nicht lange).

Vergleichen Sie dies mit dem Einfügen von Elementen am Anfang oder am Ende eines Arrays (was eine Größenänderung des Arrays am Ende oder eine Größenänderung und Verschiebung aller Elemente am Anfang erfordert).


Es ist möglich, das Einfügen von Elementen in das Ende eines Arrays auf O (1) zu setzen, wenn Sie am Ende einen Puffer mit leeren Elementen behalten, obwohl die Einfügungen gelegentlich immer noch O (1) sind. Die meisten Sammlungen tun dies. Es ist auch möglich, das Inertisieren von Elementen am Anfang eines Arrays auf O (1) zu setzen, indem Sie Ihren Indexoperator so ändern, dass die Elementnummer (n + x)% len zurückgegeben wird, wobei x die Häufigkeit ist, mit der Sie Elemente am Anfang eingefügt haben der Liste. Deques werden manchmal so implementiert (aber manchmal auch mit doppelt verknüpften Listen.
Brian
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.