Doppelt-verkettete Listen
Linked List

Was sind verkettete Listen?

Bei der verketteten Liste handelt es sich um eine dynamische Datenstruktur in der Informatik. Heißt also, dass sich die Datenstruktur innerhalb der Laufzeit des Programms an den Speicherbedarf flexibel anpassen kann. Listen sind im Allgemeinen dazu da, unterschiedliche Datentypen (Integer, String, etc.) abzuspeichern. Eine Liste besteht dabei aus mehreren Bestandteilen. Die wichtigsten sind für uns dabei zum einen der Speicher, auf dem die Elemente abgelegt werden können. Zum anderen aus Zeigern, mit denen man von einem zum nächsten Element navigieren kann. Da es aber möglich ist, dynamische Elemente hinzuzufügen und zu löschen, kann ein Element einer verketteten Liste selbst auch wieder eine Liste oder ein Array sein. Deshalb muss hauptsächlich darauf geachtet werden, dass stets genug Speicher zur Verfügung steht, um einer Liste weitere Elemente hinzuzufügen.

Doppelt verkettete Listen

Wenn sich die Liste nun noch zusätzlich um einen Verweis auf den Vorgängerknoten erweitert, dann entsteht daraus eine doppelt verkettete Liste. Folglich gibt es in solchen Listen noch einen zweiten Nullzeiger: Den, der vom ersten Element auf seinen Vorzeiger zeigt.

Aufgabenstellung

Es soll eine doppelt-verkettete Liste mit folgender Datenstruktur erstellt werden:

Structure Name: Members
Fields: Firstname (Vorname)
Lastname (Nachname)
Salutation (Anrede)
Creditlimit (float)
District (Bezirk)
Postcode (int)

Vorgegebene Aufgabenstellung als PDF

Funktionsbeschreibung des Programms

Es sollen in der Konsole die obenstehende Datenstruktur als Auswahlmenü angezeigt werden. Durch dieses kann der Benutzer eines der folgenden Menüpunkten wählen:

Auswahlmenu

Je nach dem, welcher Menüpunkt ausgewählt wird, können Daten von einer Datei gelesen bzw. geschrieben sowie aufsteigend oder absteigend sortiert werden. Außerdem können Daten (Vorname, Nachname, Anrede, Kreditlimit, Bezirk, Postleitzahl) eingegeben und somit zur "Personenliste" hinzugefügt werden. Spezielle Daten wie zum Beispiel die Anrede oder das Kreditlimit können in der dopplet verkettete Liste gesucht werden. Mit den letzten zwei Menüpunkten können auch Daten von der "Personenliste" bzw. "Teilnehmerliste" entfernt bzw. gelöscht werden. Mit dem letzten Menüpunkt wird das Programm beendet.

Die folgende Tabelle stellt beispielhafte Datensätze dar:


DatensätzeAnredeVornameNachnameKreditlimitBezirkPostleitzahl
1HerrRaphaelWudernitz4000Korneuburg2100
2HerrAlexanderThürr5000Hollabrunn2020
3FrauLillyVit2500Eggenburg3433
4HerrTimoWechselberger6000Eggenburg3433
5HerrManuelToifl3000Horn3580
6FrauDanielaSchneider9500Tulln3430
7HerrFelixBaumgartner1400Mistelbach2130
8HerrTobiasChristen1200Krems3506
9FrauMathildaGepp5000Krems3506
10HerrKevinEifinger4200Eggenburg3433
11FrauAntoniaZeiner7400Mistelbach2130
12HerrPhilippWudernitz8000Laa2136
13HerrWolfgangRapf3300Mistelbach2130
14FrauAnastasiaMeyer4300Eggenburg3433
15HerrSimonMayer6600Korneuburg2130

Lösungsweg

Jeder Menüpunkt soll in jeweils ein eigenes Unterprogramm programmiert werden und in einer Header-Datei die Struktur (verkettete Liste) sowie alle Unterprogramme eingebunden werden. Weiters ist zu erwähnen, dass jedes Unterprogramm ausreichend kommentiert werden soll.

Header-Datei

Der nachfolgende Quellcode zeigt die Header-Datei. Diese ist notwendig, um alle einzelnen Unterprogramme einzubinden und unter einer Struktur (doppelt verkettete Liste) zu programmieren.

HeaderFile

Beschreibung: Zuerst wird die doppelt verkettete Liste (typedef struct) mit einem "next" und "prev" Zeiger definiert. Mit dem "next" Pointer wird auf die nächste Liste (next) zugegriffen. Mit dem "prev" Pointer wird auf die vorherige Liste (previous) zugegriffen. Folglich werden die Prototypen der einzelnen Unterprogramme definiert, damit diese auch in anderen Dateien verwendet werden können.

Hauptprogramm

MainFile

Beschreibung: Im Hauptprogramm erfolgt die Bildschirmausgabe des Auswahlmenüs sowie die Abfrage der Menüeingabe, um herauszufinden was der Benutzer genau tun möchte. Jedes Unterprogramm wurde Call-By-Reference aufgerufen.

Eingabe

InputFile

Beschreibung: Am Anfang des Programms wird die Header-Datei und somit die Struktur eingebunden (include). Um den Speicherplatz zu reservieren wird ein weiteres Unterprogramm aufgerufen. Dieses wird weiter auf dieser Seite weiter unten beschrieben. Danach erfolgt die Eingabe des Benutzers. Zum Schluss wird ein Unterprogramm aufgerufen, welches die eingegebenen Daten (Personen bzw. Teilnehmer) sortiert in die Liste einfügt.

Reservieren

ReserveMemFile

Beschreibung: In diesem Unterprogramm wird mittels der "malloc" (memory allocation) der Speicherplatz dynamisch verwaltet. Der Zeiger (pointer) wird mit der "malloc" Funktion mit der entsprechenden Größe reserviert. Mit der if-Abfrage wird geprüft, ob der Speicherplatz korrekt reserviert wurde, oder ein Fehler aufritt. Tritt ein Fehler auf, dann wird eine Fehlermeldung ausgegeben.

Einfügen nach Nachnamen sortiert

instertNameSortedFile

Beschreibung: Dieses Unterprogramm ist der wahrscheinlichst komplizierteste Teil des Projekts, denn hier wird sehr viel mit den next und prev Pointern gearbeitet. Zunächst wird ein weiterer Hilfspointer auf den Anfang gesetzt (start). Mit der ersten if-Abfrage wird erkannt, ob die Liste leer ist. Falls diese leer sein sollte, werden die Pointer auf das Ende der Liste gesetzt. Im Falle, dass die Liste nicht leer ist, läuft eine Schleife solange, bis der Zeiger das Ende dieser Liste erreicht. Mit der Funktion strcmp() werden die Nachnamen zweier Teilnehmer (Personen) verglichen und somit alphabetisch in der Liste sortiert. Der Hilfspointer wird auf das nächste Element mit dem next Pointer gesetzt, solange das Ende nicht erreicht ist. Wenn der Hilfspointer am Anfang der Liste ist, wird der prev Pointer auf ein "anderes Ende" gesetzt, somit können die Daten am Anfang eingefügt werden. Weiters ist zu erwähnen, dass der Fall "einfügen zwischen zwei Teilnehmern" auch auszuprogrammieren ist. Das nächste Element vom vorherigen Element wird auf den Pointer gesetzt.

Ausgabe aufsteigend sortiert

sortedOutputUpFile

Beschreibung: Zuerst wird geprüft, ob überhaupt schon eine Liste existiert. Falls eine Liste schon existiert, wird der Hilfspointer auf den Anfang der Liste gesetzt. Eine Schleife läuft solange, bis das Ende der Liste erreicht wird. Mit einem weiteren Unterprogramm werden die Teilnehmer ausgegeben und der Pointer auf den nächsten Teilnehmer gesetzt.

Ausgabe absteigend sortiert

sortedOutputDownFile

Beschreibung: Die absteigende Ausgabe ist praktisch die aufsteigende Ausgabe, nur umgekehrt. Es wird nur der Hilfspointer ans Ende der Liste anstatt an den Anfang gesetzt und mit dem prev Zeiger auf den vorherigen Teilnehmer zugegriffen.

Ausgabe am Bildschirm

outputFile

Beschreibung: Dieses Unterprogramm ist für die Ausgabe am Bildschirm zuständig. Das Unterprogramm gibt nur die Daten der Teilnehmer aus (Vorname, Nachname, Anrede,...).

File lesen

readFile

Beschreibung: Nun werden Daten von einer Datei gelesen. Mithilfe des vorgegebenen Pfades der auszulesenen Datei wird mit der ersten if-Abfrage geprüft, ob dieses File überhaupt vorhanden ist. Falls dieses vorhanden ist, läuft eine Schleife solange durch das File, bis das Ende erreicht ist. Der Speicherplatz wird nun reserviert, weil die Größe der Datei unterschiedlich sein kann. Jetzt können die Daten von der Datei gelesen werden, aber nur, wenn diese in der richtigen Reihenfolge untereinander stehen. Mit dem letzten Unterprogramm-Aufruf werden die eingelesenen Daten in richtiger Reihenfolge nach Nachnamen sortiert in die Liste eingefügt.

File schreiben

writeFile

Beschreibung: Zur Fehlervermeidung wird auch hier geprüft, ob überhaupt schon eine Liste existiert. Falls eine existiert wird der Pointer auf den Anfang gesetzt. Mit dem angegebenen Pfad kann nun auf die Datei zugegriffen werden. Auch hier wird zur Fehlervermeidung geprüft, ob die Datei existiert. Die Schleife läuft solange, bis das Listenende erreicht ist. Es werden nun alle Daten der Liste auf die Datei geschrieben und der Pointer wird dann auf das nächste Element gesetzt.

Nach Anrede suchen

searchSalutationFile

Beschreibung: Dieses Unterprogramm lässt eine gewünschte Anrede der zutreffenden Personen suchen. Prinzipiell wird nur die Anrede vom Benutzer eingegeben und mit der Funktion strcmp() die gewünschte Anrede mit der nächsten in der Liste verglichen. Wenn diese übereinstimmen, werden diese mit dem Ausgabe-Unterprogramm am Bildschirm ausgegeben. Falls keine der Personen die gewünschte Anrede besitzen, wird eine Fehlermeldung ausgegeben.

Nach Kreditlimit suchen

searchCreditlimitFile

Beschreibung: Dieses Unterprogramm ist sozusagen das gleiche Programm wie das obenstehende zum Suchen der Anrede. Die einzige Änderung ist, dass mit dem Hilfspointer einfach jeder Kreditlimit verglichen wird, und zwar, in dem man diesen mit dem gewünschten Kreditlimit subtrahiert. Ist das Ergebnis null und der Kreditlimit stimmt daher überein, wird wieder durch das Ausgabe-Programm alle Personen mit dem gesuchten Kreditlimit am Bildschirm ausgegeben.

Nach Nachnamen löschen

deleteLastnameFile

Beschreibung: In diesem Unterprogramm müssen einige Fälle berücksichtigt werden:

  • Wenn es nur ein einziges Element in der Liste gibt
  • Wenn nur das erste Element gelöscht werden soll
  • Wenn nur das letzte Element gelöscht werden soll
  • Und wenn ein Element zwischen zwei Elementen gelöscht werden soll, sprich ein Element in der Mitte.

Zuerst erfolgt die Abfrage des Benutzers, welcher Nachname der Liste gelöscht wercden soll. Um den ersten Fall, also das erste Element zu löschen, zu berücksichtigen, erfolgt eine if-Abfrage, ob der Anfang gleich wie das Ende ist. In diesem Fall wird die Liste freigegeben und gelöscht. Um den Fall zu berücksichtigen, dass nur das erste Element gelöscht werden soll, wird nur geprüft, ob der Pointer am Anfang ist. Das Element vom vorherigen nächsten Zeiger wird freigegeben. Um das letzte Element zu löschen, wird der nächste vom vorherigen Zeiger freigegeben, aber nur, wenn der Pointer das Ende erreicht hat. Der letzte Fall, also ein Element zwischen zwei anderen Elementen zu löschen, erfolgt durch setzen des Hilfspointers auf den Zeigers des vorherigen Elements. Von diesem Zeiger ausgehend wird der vorherige Zeiger vom "nächsten" Zeiger auf den "vorherigen" Zeiger gesetzt und am Ende freigegeben. Am Ende des Unterprogramms erfolgt die Zuweisung des Pointers auf das nächste Element. Wie man sehen kann, ist dies sehr verwirrend und kompliziert, da man viele Pointer-Fehler machen kann. Aus diesem Grund gibt es in C++ schon fertig programmierte und durch includes zur Verfügung stehende Funktionen zur Listenorganisation (iterator).

Nach Bezirk löschen

deleteDistrictFile

Beschreibung: Die Funktion dieses Unterprogramms ist die gleiche wie die obenstehende vom "Nach Nachnamen löschen", nur dass anstatt des Nachnamen der gewünschte Bezirk (district) gelöscht wird.

Speicher freigeben

freeMemoryFile

Beschreibung: Damit die Speicherfreigabe nicht einzelnd in mehreren Unterprogrammen auszuprogrammieren ist, wurde dies auch in einem eigenen Unterprogramm programmiert. Hier wird einfach nur der Pointer freigegeben mit der free() Funktion.



Ergebnisse

Team / Mitwirkende dieses Projekts

Dieses Projekt wurde von mir, Raphael Wudernitz, unter Aufsicht von Prof. Geischläger und Prof. Jedlicka geleitet.

Kontakt

Telefonnummer: +43 676 6080904

E-Mail: Raphael Wudernitz, raphael@wudernitz.at

Impressum

Das verwenden der auf dieser Homepage angezeigten Bilder (Quellcode) sowie Videos sind ohne meiner ausdrücklichen Erlaubnis untersagt. Bei Fragen oder Erlaubnis können Sie mich gerne kontaktieren.

Verantwortlich für den Inhalt dieser Webseite siehe Kontakt.