Menu
Coddy logo textTech

Verkettete Liste

Zuletzt aktualisiert

Eine verkettete Liste speichert eine Folge als Kette von Knoten, wobei jeder Knoten einen Wert und einen Zeiger auf den nächsten Knoten enthält. Anders als bei einem Array liegen die Knoten nicht zusammenhängend im Speicher - man folgt den next-Zeigern, um die Liste zu durchlaufen. Drücke oben auf Abspielen, um zu sehen, wie Knoten am Kopf und am Ende verknüpft werden, wie ein Wert durch Durchlaufen der Kette gesucht und wie ein Knoten durch Umverdrahten eines Zeigers entfernt wird.

Einfügen oder Löschen am Kopf ist O(1), weil du nur den Kopf neu setzt. Eine Position in der Mitte oder das Ende zu erreichen ist O(n), weil du erst dorthin laufen musst. Dieser Kompromiss -günstige Enden, kein wahlfreier Zugriff- unterscheidet eine verkettete Liste von einem Array.

Zeitkomplexität

OperationKomplexitätAnmerkungen
Am Kopf einfügenO(1)Kopf neu setzen
Am Ende einfügenO(n)Erst zum Ende laufen (O(1) mit einem Endzeiger)
SuchenO(n)Den next-Zeigern folgen
Kopf löschenO(1)Kopf über ihn hinaus neu setzen
Zugriff per IndexO(n)Kein wahlfreier Zugriff

Verkettete Liste vs Array

AspektVerkettete ListeArray
SpeicherVerstreute Knoten + ZeigerZusammenhängender Block
Wahlfreier ZugriffO(n)O(1)
Einfügen/Löschen am AnfangO(1)O(n) (Verschieben)
Cache-FreundlichkeitSchlechtGut

Durchgerechnetes Beispiel

Die Liste [10, 20] aufbauen, 5 voranstellen, dann 20 löschen:

SchrittStrukturAktion
Starthead -> nullLeere Liste
Am Kopf 10 einfügenhead -> 10 -> nullKopf auf einen neuen Knoten setzen, dessen next der alte Kopf (null) ist
Am Ende 20 einfügenhead -> 10 -> 20 -> nullZum Knoten 10 laufen, dessen next auf einen neuen Knoten 20 setzen
Am Kopf 5 einfügenhead -> 5 -> 10 -> 20 -> nullDer neue Knoten 5 zeigt auf den alten Kopf 10; der Kopf zeigt nun auf 5
20 löschenhead -> 5 -> 10 -> nullZu 10 laufen, dessen next von 20 auf null setzen; Knoten 20 ist ausgehängt

Wann eine verkettete Liste verwenden

Verwenden, wennVermeiden, wenn
Du an den Enden (oder an einem gehaltenen Knoten) weit häufiger einfügst oder löschst, als du indizierstDu schnellen wahlfreien Zugriff per Position brauchst (O(1)-Indizierung)
Sich die Größe stark ändert und du keine Kosten für Neuallokation/Kopieren willstDu enge Schleifen durchläufst, in denen Cache-Lokalität die Leistung bestimmt
Du eine Warteschlange, einen Stapel oder eine Adjazenzliste baustDer Speicher knapp ist - jeder Knoten kostet einen zusätzlichen Zeiger pro Element
Du stabile Referenzen auf Knoten brauchst, die Einfügungen anderswo überstehenDu meist liest und selten änderst, sodass ein Array einfacher und schneller ist

Linked List-Code

Eine saubere, lauffähige Linked List-Implementierung in Python, JavaScript, Java, C++, C. Wähle eine Sprache, kopiere den Code oder öffne ihn vorgeladen im Coddy-Playground.

Linked List-Code in Python

Python
1class Node:2    def __init__(self, value):3        self.value = value4        self.next = None5
6
7class LinkedList:8    def __init__(self):9        self.head = None10
11    def append(self, value):12        node = Node(value)13        if self.head is None:14            self.head = node15            return16        current = self.head17        while current.next:18            current = current.next19        current.next = node20
21    def find(self, value):22        current = self.head23        while current:24            if current.value == value:25                return True26            current = current.next27        return False28
29    def delete(self, value):30        # Re-link the previous node around the match31        current, prev = self.head, None32        while current:33            if current.value == value:34                if prev is None:35                    self.head = current.next36                else:37                    prev.next = current.next38                return True39            prev, current = current, current.next40        return False41
42    def __str__(self):43        values, current = [], self.head44        while current:45            values.append(str(current.value))46            current = current.next47        return " -> ".join(values) + " -> None"48
49
50lst = LinkedList()51for value in [3, 7, 1, 9]:52    lst.append(value)53
54print("List:    ", lst)55print("find(7): ", lst.find(7))56lst.delete(1)57print("After delete(1):", lst)
Führe diesen Code im Python-Playground aus

FAQ zu verketteten Listen

Was ist der Unterschied zwischen einer verketteten Liste und einem Array?
Ein Array speichert die Elemente in einem einzigen zusammenhängenden Block und bietet so O(1)-Wahlzugriff, aber O(n)-Einfügungen/Löschungen, die Elemente verschieben. Eine verkettete Liste speichert Knoten irgendwo im Speicher, verbunden durch Zeiger, und bietet O(1)-Einfügungen/Löschungen an einer bekannten Position, aber O(n)-Zugriff, da man die Kette durchlaufen muss.
Wann sollte ich eine verkettete Liste verwenden?
Verkettete Listen glänzen, wenn du häufig an den Enden (oder an einem Knoten, dessen Referenz du bereits hältst) einfügst oder entfernst und selten wahlfreien Zugriff brauchst - zum Beispiel Warteschlangen, Stapel, Adjazenzlisten und LRU-Caches. Wenn du schnellen indizierten Zugriff brauchst, ist ein Array oder dynamisches Array meist besser.
Wie ist die Zeitkomplexität einer verketteten Liste?
Einfügen oder Löschen am Kopf ist O(1). Suchen oder das Ende ohne Endzeiger zu erreichen ist O(n), weil man den next-Zeigern einzeln folgt. Es gibt keinen O(1)-Wahlzugriff - das Indizieren in eine verkettete Liste ist O(n).
Was ist der Unterschied zwischen einer einfach und einer doppelt verketteten Liste?
Eine einfach verkettete Liste speichert pro Knoten nur einen next-Zeiger, sodass man in eine Richtung laufen kann und das Löschen eines Knotens eine Referenz auf seinen Vorgänger erfordert. Eine doppelt verkettete Liste fügt einen prev-Zeiger hinzu, was Rückwärtsdurchlauf und das O(1)-Löschen eines gehaltenen Knotens ermöglicht - zum Preis eines zusätzlichen Zeigers pro Knoten und mehr Verwaltungsaufwand bei jedem Einfügen und Löschen.
Warum ist Einfügen am Ende O(n), wenn Einfügen am Kopf O(1) ist?
Einfügen am Kopf verdrahtet nur den head-Zeiger neu, was konstante Arbeit ist. Das Ende zu erreichen bedeutet, den next-Zeigern vom Kopf bis zum Ende zu folgen, was O(n) ist. Ein separater tail-Zeiger macht auch das Einfügen am Ende O(1), weshalb reale Listen oft beide Enden verfolgen.
Haben verkettete Listen überall O(1)-Einfügung?
Nein - das ist ein verbreiteter Irrtum. Das Einfügen selbst ist nur dann O(1), wenn du bereits einen Zeiger auf den Knoten hältst, nach dem du einfügst. Diese Position per Wert oder Index zu finden kostet weiterhin O(n), weil du die Kette durchlaufen musst, um dorthin zu gelangen.
Coddy programming languages illustration

Meistere Algorithmen mit Coddy

LOS GEHT'S