Die Implementierung verknüpfter Listen in Python verstehen

Datenstrukturen sind von zentraler Bedeutung in der Welt der Programmierung. Sie ermöglichen es uns, Daten so zu organisieren, dass sie effizient verarbeitet werden können.

In dieser Anleitung werden wir uns mit der einfach verketteten Liste und der doppelt verketteten Liste beschäftigen.

Eine verkettete Liste ist eine lineare Datenstruktur. Im Gegensatz zu Arrays werden Daten nicht in aufeinanderfolgenden Speicherbereichen abgelegt. Stattdessen wird jedes Element als Knoten bezeichnet, und diese Knoten sind durch Zeiger miteinander verbunden. Der erste Knoten in der Liste wird als Kopf bezeichnet.

Die Größe einer verketteten Liste ist dynamisch, was bedeutet, dass wir beliebig viele Knoten hinzufügen können, solange der Speicher des Geräts dies zulässt.

Es gibt zwei Haupttypen von verketteten Listen. Lassen Sie uns diese nacheinander genauer betrachten.

Einfach verkettete Liste

Eine einfach verkettete Liste verwendet einen einzigen Zeiger, der auf den nächsten Knoten in der Liste verweist. Jeder Knoten enthält also die Daten und einen Zeiger zum nächsten Knoten.

Der letzte Knoten in einer einfach verketteten Liste hat einen Null-Zeiger als Nachfolger, um das Ende der Liste zu kennzeichnen.

Sie können sich die Darstellung einer solchen Verknüpfung unten ansehen.

Nachdem wir nun ein gutes Verständnis von einfach verketteten Listen haben, schauen wir uns an, wie man sie in Python implementiert.

Implementierung einer einfach verketteten Liste

1. Der erste Schritt ist die Erstellung der Knotenstruktur für die Liste.

Wie geht das?

In Python können wir einen Knoten mit Hilfe einer Klasse definieren. Diese Klasse speichert die Daten und den Zeiger auf den nächsten Knoten.

Hier ist der Code für den Knoten:

class Node:
    def __init__(self, data):
      ## Daten des Knotens
      self.data = data
      ## Zeiger auf den nächsten Knoten
      self.next = None
  

Mit dieser Klasse können wir Knoten mit beliebigen Datentypen erstellen. Das werden wir gleich sehen.

Jetzt, da wir die Knotenstruktur haben, brauchen wir eine Möglichkeit, eine verkettete Liste aus mehreren Knoten zu erstellen. Dazu erstellen wir eine weitere Klasse für die gesamte Liste.

2. Erstellen Sie eine Klasse namens LinkedList, in der der Kopf (head) standardmäßig auf None gesetzt ist. Siehe den Code unten:

class LinkedList:
    def __init__(self):
      ## Initialisierung des Kopfes mit None
      self.head = None
  

3. Nun haben wir sowohl die Node- als auch die LinkedList-Klasse. Wie fügen wir nun einen neuen Knoten in die Liste ein? Eine einfache Möglichkeit wäre, eine Methode in der LinkedList-Klasse zu verwenden. Ja, das ist eine gute Idee. Schreiben wir also eine Methode, um einen neuen Knoten in die Liste einzufügen.

Um einen neuen Knoten in die Liste einzufügen, müssen wir folgende Schritte befolgen:

Schauen wir sie uns an:

  • Überprüfen, ob der Kopf leer ist (also die Liste).
  • Wenn der Kopf leer ist, weisen Sie den neuen Knoten dem Kopf zu.
  • Wenn der Kopf nicht leer ist, suchen Sie den letzten Knoten der Liste.
  • Weisen Sie den neuen Knoten dem next-Zeiger des letzten Knotens zu.

Hier ist der Code zum Einfügen eines neuen Knotens in die verkettete Liste:

class LinkedList:
    ####
    def insert(self, new_node):
      ## Überprüfen, ob der Kopf leer ist
      if self.head:
        ## Suche nach dem letzten Knoten
        last_node = self.head
        while last_node.next != None:
          last_node = last_node.next
        ## Zuweisen des neuen Knotens zum next-Zeiger des letzten Knotens
        last_node.next = new_node
      else:
        ## Der Kopf ist leer
        ## Zuweisen des Knotens zum Kopf
        self.head = new_node
  

Super! Wir haben die Methode zum Einfügen eines neuen Knotens in die Liste fertiggestellt. Wie können wir nun auf die Daten der Knoten in der Liste zugreifen?

Um auf die Daten zuzugreifen, müssen wir der Verkettung durch die next-Zeiger folgen, so wie wir es getan haben, um den letzten Knoten in der Einfügemethode zu finden. Schreiben wir also eine Methode in der LinkedList-Klasse, um alle Knotendaten der Liste auszugeben.

4. Hier sind die Schritte zum Anzeigen aller Knotendaten der Liste:

  • Initialisieren Sie eine Variable mit dem Kopf.
  • Schreiben Sie eine Schleife, die so lange ausgeführt wird, bis das Ende der Liste erreicht ist.
    • Geben Sie die Knotendaten aus.
    • Gehen Sie zum nächsten Zeiger.

Hier ist der Code:

class LinkedList:
    ####
    def display(self):
      ## Variable für die Iteration
      temp_node = self.head
      ## Iterieren, bis das Ende der Liste erreicht ist
      while temp_node != None:
        ## Ausgeben der Knotendaten
        print(temp_node.data, end='->')
        ## Weitergehen zum nächsten Knoten
        temp_node = temp_node.next
      print('Null')
  

Puh! Wir haben die Liste mit den benötigten Methoden erstellt. Testen wir nun die Liste, indem wir sie mit einigen Daten instanziieren.

Wir können einen Knoten mit dem Code Node(1) erstellen. Hier ist der vollständige Code der Implementierung der Liste zusammen mit einem Beispiel:

class Node:
    def __init__(self, data):
      ## Daten des Knotens
      self.data = data
      ## Zeiger auf den nächsten Knoten
      self.next = None
  class LinkedList:
    def __init__(self):
      ## Initialisierung des Kopfes mit None
      self.head = None
    def insert(self, new_node):
      ## Überprüfen, ob der Kopf leer ist
      if self.head:
        ## Suche nach dem letzten Knoten
        last_node = self.head
        while last_node.next != None:
          last_node = last_node.next
        ## Zuweisen des neuen Knotens zum next-Zeiger des letzten Knotens
        last_node.next = new_node
      else:
        ## Der Kopf ist leer
        ## Zuweisen des Knotens zum Kopf
        self.head = new_node
    def display(self):
      ## Variable für die Iteration
      temp_node = self.head
      ## Iterieren, bis das Ende der Liste erreicht ist
      while temp_node != None:
        ## Ausgeben der Knotendaten
        print(temp_node.data, end='->')
        ## Weitergehen zum nächsten Knoten
        temp_node = temp_node.next
      print('Null')
  if __name__ == '__main__':
    ## Instanziieren der Liste
    linked_list = LinkedList()
    ## Einfügen von Daten in die Liste
    linked_list.insert(Node(1))
    linked_list.insert(Node(2))
    linked_list.insert(Node(3))
    linked_list.insert(Node(4))
    linked_list.insert(Node(5))
    linked_list.insert(Node(6))
    linked_list.insert(Node(7))
    ## Ausgeben der Liste
    linked_list.display()
  

Führen Sie das obige Programm aus, um folgende Ausgabe zu erhalten:

1->2->3->4->5->6->7->Null

Das war es mit der einfach verketteten Liste. Jetzt wissen Sie, wie man eine solche Liste implementiert. Mit diesem Wissen können Sie die doppelt verkettete Liste leicht implementieren. Gehen wir nun zum nächsten Abschnitt dieser Anleitung.

Doppelt verkettete Liste

Eine doppelt verkettete Liste verwendet zwei Zeiger: einen, der auf den vorherigen Knoten und einen, der auf den nächsten Knoten in der Liste zeigt. Jeder Knoten enthält also die Daten sowie zwei Zeiger.

Der previous-Zeiger des ersten Knotens und der next-Zeiger des letzten Knotens sind jeweils null, um die Enden der Liste zu markieren.

Sie können sich die Darstellung einer solchen Verknüpfung unten ansehen.

Die Implementierung einer doppelt verketteten Liste ähnelt der einer einfach verketteten Liste. Die wiederholte Erklärung wäre wahrscheinlich langweilig für Sie. Gehen Sie stattdessen einfach den Code Schritt für Schritt durch, und Sie werden es schnell verstehen. Los geht’s!

Implementierung einer doppelt verketteten Liste

1. Erstellen einer Knotenstruktur für die doppelt verkettete Liste, die einen Zeiger zum vorherigen Knoten, die Daten und einen Zeiger zum nächsten Knoten enthält.

class Node:
    def __init__(self, data):
      ## Zeiger zum vorherigen Knoten
      self.prev = None
      ## Daten des Knotens
      self.data = data
      ## Zeiger zum nächsten Knoten
      self.next = None
  

2. Klasse für die doppelt verkettete Liste.

class LinkedList:
    def __init__(self):
      ## Initialisierung des Kopfes mit None
      self.head = None
  

3. Eine Methode zum Einfügen eines neuen Knotens in die doppelt verkettete Liste.

class LinkedList:
    ####
    def insert(self, new_node):
      ## Überprüfen, ob der Kopf leer ist
      if self.head:
        ## Suchen nach dem letzten Knoten
        last_node = self.head
        while last_node.next != None:
          last_node = last_node.next
        ## Zuweisen des letzten Knotens zum previous-Zeiger des neuen Knotens
        new_node.prev = last_node
        ## Zuweisen des neuen Knotens zum next-Zeiger des letzten Knotens
        last_node.next = new_node
  

4. Eine Methode zum Ausgeben der Daten der doppelt verketteten Liste.

class LinkedList:
    ####
    def display(self):
      ## Ausgeben der Daten in normaler Reihenfolge
      print("Normale Reihenfolge: ", end='')
      temp_node = self.head
      while temp_node != None:
        print(temp_node.data, end=' ')
        temp_node = temp_node.next
      print()
      ## Ausgeben der Daten in umgekehrter Reihenfolge mit dem previous-Zeiger
      print("Umgekehrte Reihenfolge: ", end='')
      ## Suchen nach dem letzten Knoten
      last_node = self.head
      while last_node.next != None:
        last_node = last_node.next
      temp_node = last_node
      while temp_node != None:
        print(temp_node.data, end=' ')
        temp_node = temp_node.prev
      print()
  

Wir haben den Code für die doppelt verkettete Liste gesehen. Es gibt keinen Code zur Verwendung der Klasse für die doppelt verkettete Liste – das ist Ihre Aufgabe! Verwenden Sie die Klasse für die doppelt verkettete Liste auf ähnliche Weise wie die einfach verkettete Liste. Viel Spaß dabei! 🙂

Fazit

Es gibt viele Probleme, die man mithilfe von verketteten Listen lösen kann. Sie müssen jedoch die grundlegende Implementierung, die wir in dieser Anleitung besprochen haben, beherrschen. Ich hoffe, Sie haben beim Lernen dieses neuen Konzepts viel Spaß gehabt.

Viel Spaß beim Programmieren! 🙂