Liste (Datenstruktur)

Liste (Datenstruktur)

Die Verkettete Liste ist eine dynamische Datenstruktur, die eine Speicherung von einer im Vorhinein nicht bestimmten Anzahl von miteinander in Beziehung stehenden Objekten erlaubt. Sie wird durch Zeiger auf die jeweils folgende(n) Knoten oder Speicherzellen des Arbeitsspeichers realisiert.

Inhaltsverzeichnis

Vergleich mit anderen Datenstrukturen

Im Gegensatz zu Arrays müssen die einzelnen Speicherzellen nicht nacheinander im Speicher abgelegt sein, es kann also nicht mit einfacher Adressarithmetik gearbeitet werden, sondern die Speicherorte müssen absolut referenziert werden. Der Vorteil einer Liste gegenüber einem Array ist, dass das Einfügen und Löschen von Elementen an jedem Punkt in der Liste in konstanter Zeit möglich ist. Dies liegt daran, dass nur Verweise neu gesetzt werden und nicht wie bei Arrays alle nachfolgende Elemente verschoben werden müssen. Die Liste verbraucht jedoch mehr Speicher, da die Verweise auf andere Listenelementen gespeichert werden müssen.

Im Gegensatz zu Bäumen sind Listen linear, das heißt ein Element hat genau einen Nachfolger. Eine Liste kann zu einem Zyklus (zyklische Liste) geschlossen werden, indem der Zeiger des letzten Listenelementes so geändert wird, dass er auf ein beliebiges Listenelement zeigt. Dieses Listenelement (und nur dieses) hat dann zwei Vorgänger, alle anderen Elemente haben genau einen Vorgänger. Ob eine Liste zyklisch ist oder nicht, kann mit dem Hase-Igel-Algorithmus effizient festgestellt werden.

Knoten

Ein Knoten (engl. node) bezeichnet ein Element der Liste, welches die Daten und einen Zeiger auf seinen Nachfolger enthält.

Ein Knoten: Datenteil und Zeiger

Typen

Man unterscheidet grundsätzlich zwischen einfach und mehrfach oder doppelt verketteten Listen.

Einfach verkettete Liste

Sie ist die einfachste Form der verketteten Liste, da sie neben den einzelnen Knoten nur einen „Start“-Zeiger besitzt. Dieser zeigt auf den ersten Knoten in der Liste. Der letzte Knoten in der Liste zeigt auf den Wert NULL (hier NIL für „Not In List”).

Verbale Definition: Eine Liste ist entweder leer oder sie besteht aus einem Kopf und einem Zeiger, der wiederum eine Liste ist.

Einfach verkettete Liste mit drei Werten
Einfach verkettete Liste mit fünfzehn Werten

Vorteile

  • Elemente können sehr schnell am Anfang der Liste eingefügt werden; sehr geringer Speicherbedarf
  • Im Gegensatz zu einem Array ist das Einfügen problemlos möglich, ohne dass der komplette Datensatz bei jeder Vergrößerung umkopiert werden muss

Nachteile

  • Es ist aufwändig nach Daten zu suchen, Knoten einzufügen, zu löschen und die Liste zu sortieren, da über jedes einzelne Element gegangen (iteriert) werden und das Einfügen an der ersten und der letzten Stelle gesondert behandelt werden muss.

Erweiterung

Da sich die zuvor erwähnte Variante als zu umständlich (siehe Nachteile) erwiesen hat, wird als Erweiterung ein Zeiger auf das letzte Element eingeführt. Da man zum Anfügen an das Ende der Liste das letzte Element braucht, damit es auf die neuen Elemente zeigt, kann man dadurch schnell Elemente anfügen. In der normalen, einfach verketteten Liste müsste man sonst das erste Element nehmen und bis zum letzten durchgehen. Nachteilig wirkt sich ein etwas größerer Speicherbedarf aus, da man auch einen Zeiger auf das letzte Element speichern muss.

Doppelt (mehrfach) verkettete Liste

Doppelt verkettete Liste mit drei Werten

Im Gegensatz zur einfach-verketteten Liste hat jedes Element sowohl einen Zeiger auf das nachfolgende als auch auf das vorhergehende Element.

Der Vorgänger-Zeiger des ersten und der Nachfolger-Zeiger des letzten Elementes zeigen auf den Wert NULL. Dieses besondere Element dient zum Auffinden des Anfangs und des Endes einer doppelt verketteten Liste.

Vorteile

  • Die Elemente in der zweiten Hälfte einer sortierten Liste sind schneller aufzufinden.
  • Schnelles Löschen und Einfügen von Elementen.
    • Das macht zum Beispiel das effiziente Sortieren der Liste möglich.
  • Über die Liste kann von hinten nach vorne iteriert werden.

Nachteile

  • Höherer Speicherbedarf für die zusätzlichen Zeiger.

Weitere Formen

Liste mit Listenkopf

Der Listenkopf oder Sentinel ist ein spezieller Knoten am Anfang der Liste, der zusätzliche Informationen, wie beispielsweise die Anzahl der Knoten in der Liste, enthält. Der wesentliche Vorteil ist, dass das Einfügen des 1. Elements kein Sonderfall ist.

Skip-Liste

Wie verkettete Listen werden auch bei der Skipliste die Daten in Containern abgelegt. Diese enthalten einen Schlüssel und einen Zeiger auf den nächsten Container. Allerdings können Container in Skiplisten auch Zeiger auf andere Container enthalten, welche nicht direkt nachfolgen. Es können also Schlüssel übersprungen werden. Jeder Container hat eine bestimmte Höhe h, welche um 1 kleiner ist als die Anzahl der Zeiger, die ein Container enthält. Die Zeiger werden von 0 bis h nummeriert. Grundsätzlich imitiert eine Skipliste also die Binäre Suche auf einem Feld.

Skip-Liste mit mehreren Sprüngen

Bei den Skip-Listen unterscheidet man drei Arten von Typen:

  1. ausgeglichene SkipList
  2. unausgeglichene SkipList (siehe Bild)
  3. randomisierte SkipList

Bei allen Typen ist das mehrfache Auftreten des Inhaltes erlaubt. Allerdings sind die aus- und die unausgeglichene SkipList geordnet, wohingegen die randomisierte SkipList nicht notwendigerweise geordnet sein muss. Durch das Einfügen von Zwischenstationen, welches den Aufwand der Implementierung erhöht, kann die mittlere Zugriffszeit (und damit verbunden die Komplexität) gesenkt werden. Eine Erweiterung des SkipList-Prinzip ist die Verknüpfung mit dem Prinzip der doppelt verknüpften Liste, welche die Möglichkeit eines „Rücksprunges“ ermöglicht. Bei einer ausgeglichenen SkipList senkt dies allerdings nicht die Komplexität, wohingegen bei einer unausgeglichenen SkipList auf Zwischenstationen verzichtet werden kann, welches dann wiederum den Zugriff auf Elemente in der Nähe der nächsten Zwischenstation erhöht.

Die Operationen Einfügen, Suchen und Löschen haben jeweils eine erwartete Laufzeit von \mathcal{O}(\log n).

Berechnung der Containerhöhe

Bei der randomisierten Skipliste erfolgt die Berechnung der Höhe h nach dem Zufallprinzip. Die Wahrscheinlichkeit, dass eine bestimmte Höhe erreicht wird, kann folgendermaßen ermittelt werden: \frac{1}{2\cdot 2^h}

Bei nicht randomisierten Skiplisten wird die Höhe so bestimmt, dass jeder Zeiger mit Zeigerhöhe z auf einen Container 2z Positionen weiter hinten in der Liste zeigen kann – also alle Container dazwischen eine geringere Höhe haben als der Zeiger.

Adaptive Listen

Da der Aufwand des Zugriffes auf ein Element einer einfach verknüpften Liste mit der Entfernung vom Start um n Schritte zunimmt, kam man auf das Prinzip der adaptiven Listen. Dieses Prinzip geht von der Voraussetzung des zunehmenden Zugriffsaufwandes aus und sortiert die Listenelemente nach ihrer Zugriffshäufigkeit (vergangenheitssortiert). Dabei gibt es drei grundsätzliche Strategien:

  • MoveToFront: Bei jedem Zugriff auf ein Element wird dieses entfernt und am Anfang der Liste eingefügt.
  • Transpose: Bei jedem Zugriff auf ein Element wird es mit seinem Vorgänger vertauscht (Sonderfall: 1. Element)
  • Gratification: Zu jedem Element werden dessen Zugriffshäufigkeit gespeichert. In einem bestimmten Intervall wird anhand der Zugriffshäufigkeit die Liste neu sortiert.

Listen in der objektorientierten Programmierung

In der objektorientierten Programmierung zeichnen sich Listen gemäß dem Prinzip der Kapselung durch eine Menge von Listenoperationen aus. Intern können dabei unterschiedliche und durchaus auch kompliziertere Datenstrukturen, wie binäre Bäume zum Einsatz kommen. Aufgrund der internen Datenstruktur können dabei oft auch weitere Funktionen, wie beispielsweise Sortierung, sortiertes Einfügen, Entfernen des größten Elementes, etc. angeboten werden.

Je nach Einsatzzweck kann es sinnvoll sein, zwischen konkreten Implementierungen der Schnittstelle Liste zu wählen. Wird beispielsweise hauptsächlich wahlfrei über Index auf die Liste zugegriffen, wäre eine verkettete Liste eine schlechte Wahl, da dort n Operationen nötig sind, um das nte Element zu adressieren.

Daher werden in objektorientierten Bibliotheken oft neben der Schnittstelle verschiedene konkrete Implementierungen angeboten. Beispielsweise gibt es in der Programmiersprache Java als Schnittstelle java.util.List und als konkrete Implementierungen unter anderem java.util.LinkedList und java.util.ArrayList. In C++ werden Listen und Vektoren in der Standardbibliothek implementiert.

Listen in der Programmiersprache LISP

Listen sind neben den Einzelwerten (Atomen) die Basisdatenstruktur der Programmiersprache LISP. Programme sind Listen von Listen.

Beispiele

Nachfolgend Beispiele in Pseudocode (an C angelehnt). Dabei gibt es in der Liste das Feld Key (also den Schlüssel, den man sucht) und das Feld Data (die Nutzdaten, die hinter dem Schlüssel stecken). Das Beispiel:

  • Gib mir die Lottozahlen vom 1. Januar 2006. Dabei ist 1. Januar 2006 der Schlüssel und die Lottozahlen sind die Nutzdaten.
  • pNext in einem Knoten (Element) ist der Zeiger auf das nächste Element in der Liste

Neues Element in Liste einfügen

   FUNKTION insertElement( Datum, Lottozahlen )
   {
      Zeiger *NeuesElement = Speicherallozierung(size(Datum) + size(Lottozahlen));
 
      WENN (Speicherallozierung erfolgreich)
      {
         Kopieren von Datum und Lottozahlen nach *NeuesElement;
         NeuesElement->pNext = NULL;
         Zeiger *LetztesElement = FindeLetztesElement();
 
         WENN (Letztes Element gefunden)
         {
            LetztesElement->pNext = NeuesElement;
            return GELUNGEN;
         }
         SONST
         {
            return FEHLER;
         }
 
      }
      SONST
      {
         return FEHLER;
      }
   }

Element suchen

   FUNKTION sucheElement( Datum )
   {
 
      Variable lz = NULL;
      Zeiger *AktuellesElement = ErstesElement();
 
      WENN (Erstes Element gefunden)
      {
         SOLANGE (lz == NULL UND AktuellesElement != NULL)
         {
            WENN (AktuellesElement->Datum == Datum)
               lz = AktuellesElement->Lottozahlen;
            SONST
               AktuellesElement = AktuellesElement->pNext;
         }
 
         WENN (lz != NULL)
            return lz;
         SONST
            return FEHLER;
      }
      SONST
      {
         return FEHLER;
      }
   }

Element aus Liste löschen

   FUNKTION deleteElement( Datum )
   {
      Zeiger *aktuellesElement = NULL;
      Zeiger *nächstesElement = ErstesElement();
 
      SOLANGE ( nächstesElement != NULL )
      {
         WENN (nächstesElement->Datum == Datum) // ''zu löschendes Element gefunden''
         {
            WENN ( aktuellesElement != NULL ) // ''wir befinden uns nicht am Listenbeginn''
            {
               WENN ( nächstesElement->pNext != NULL) // ''wir befinden uns nicht am Listenende''
               {
                  aktuellesElement->pNext = nächstesElement->pNext;
                  Lösche nächstesElement;
               }
               SONST // ''wir befinden uns am Listenende''
               {
                  aktuellesElement->pNext = NULL;
                  Lösche nächstesElement;
               }
            }
            SONST // ''wir befinden uns am Listenbeginn''
            {
               WENN ( nächstesElement->pNext != NULL) // ''wir befinden uns nicht am Listenende''
                  Setze Listenbeginn auf nächstesElement->pNext;
               SONST // ''wir befinden uns am Listenende - wir löschen das einzige Element, Liste ist nun leer''
                  Setze Listenbeginn auf NULL;
 
               Lösche nächstesElement;
            }
         }
         AktuellesElement = nächstesElement;
         nächstesElement = nächstesElement->pNext;
      }
   }

Verwendung von Liste in objektorientierter Sprache

Dieses Beispiel zeigt die Verwendungen einer Liste in C++.

#include <iostream>
#include <list>
 
using namespace std;
 
...
 
 // Initialisierung
 list<int> nummern;
 
 // am Anfang einfügen
 nummern.push_front(4);
 nummern.push_front(3);
 
 // am Ende anfügen
 nummern.push_back(5);
 nummern.push_back(6);
 
 // die Liste enthält 3 4 5 6
 // Durchlaufen der Liste
 for(list<int>::const_iterator it = nummern.begin(); it != nummern.end(); ++it)
 {
    cout << *it << " ";
 }
 
        // komplexes Beispiel zum entfernen von Zahlen größer als 4
 nummern.erase( remove_if(nummern.begin(), nummern.end(), bind2nd(greater<int>(), 4) ), nummern.end() );
 
        // sortieren
 nummern.sort( );
 
        //löschen
 nummern.clear();

Weblinks


Wikimedia Foundation.

Игры ⚽ Нужна курсовая?

Schlagen Sie auch in anderen Wörterbüchern nach:

  • Liste — (von italienisch lista „Leiste, Papierstreifen“) bezeichnet eine Sammlung von Daten/Informationen über – thematisch meist zusammengehörende – Begriffe und deren Darstellung in einer einheitlichen, sich ständig wiederholenden Form, beispielsweise… …   Deutsch Wikipedia

  • Datenstruktur — In der Informatik ist eine Datenstruktur ein mathematisches Objekt zur Speicherung von Daten. Es handelt sich um eine Struktur, weil die Daten in einer bestimmten Art und Weise angeordnet und verknüpft werden, um den Zugriff auf sie und ihre… …   Deutsch Wikipedia

  • Liste — Verzeichnis; Aufstellung; Tabelle; Register; Datenbank; Aufzählung; Gliederung; Auflistung; verkettete Liste * * * Lis|te [ lɪstə], die; , n: [alphabetisch in Form einer Tabelle angeordnete] Zusammenstel …   Universal-Lexikon

  • Liste von Abkürzungen (Computer) — Dies ist eine Liste technischer Abkürzungen, die im IT Bereich verwendet werden. Inhaltsverzeichnis A B C D E F G H I J K L M N O P Q R S T U V W X Y Z siehe auch: Liste von Dateiendu …   Deutsch Wikipedia

  • Liste von Algorithmen — Dies ist eine Liste von Artikeln zu Algorithmen in der deutschsprachigen Wikipedia. Siehe auch unter Datenstruktur für eine Liste von Datenstrukturen. Inhaltsverzeichnis 1 Klassen von Algorithmen nach Komplexität 2 Klassen von Algorithmen nach… …   Deutsch Wikipedia

  • Liste bedeutender Personen für die Informatik — Dieser Artikel wurde aufgrund von inhaltlichen Mängeln auf der Qualitätssicherungsseite der Redaktion Informatik eingetragen. Dies geschieht, um die Qualität der Artikel aus dem Themengebiet Informatik auf ein akzeptables Niveau zu bringen. Hilf… …   Deutsch Wikipedia

  • Doppelt verkettete Liste — Verkettete Listen gehören zu den dynamischen Datenstrukturen, die eine Speicherung von einer im Vorhinein nicht bestimmten Anzahl von miteinander in Beziehung stehenden Werten einfacher oder zusammengesetzter Datentypen erlauben. Sie werden durch …   Deutsch Wikipedia

  • Einfach verkettete Liste — Verkettete Listen gehören zu den dynamischen Datenstrukturen, die eine Speicherung von einer im Vorhinein nicht bestimmten Anzahl von miteinander in Beziehung stehenden Werten einfacher oder zusammengesetzter Datentypen erlauben. Sie werden durch …   Deutsch Wikipedia

  • Lineare Liste — Verkettete Listen gehören zu den dynamischen Datenstrukturen, die eine Speicherung von einer im Vorhinein nicht bestimmten Anzahl von miteinander in Beziehung stehenden Werten einfacher oder zusammengesetzter Datentypen erlauben. Sie werden durch …   Deutsch Wikipedia

  • Verkettete Liste — Verkettete Listen gehören zu den dynamischen Datenstrukturen, die eine Speicherung von einer im Vorhinein nicht bestimmten Anzahl von miteinander in Beziehung stehenden Werten einfacher oder zusammengesetzter Datentypen erlauben. Sie werden durch …   Deutsch Wikipedia

Share the article and excerpts

Direct link
Do a right-click on the link above
and select “Copy Link”