Virtuelle Lehrerweiterbildung Informatik in Niedersachsen Kerstin Strecker Graphen – Teil 1 Graphen Teil 1 1. Graphen als Modellierungswerkzeug 2. Darstellung von Graphen (Adjazenzmatrix und Adjazenzliste) 3. Ein bisschen Graphentheorie 4. Gewichtete Graphen und Dijkstra S. 1 Virtuelle Lehrerweiterbildung Informatik in Niedersachsen Kerstin Strecker Graphen – Teil 1 S. 2 1. Graphen als Modellierungswerkzeug Im ersten Beispiel beschäftigen wir uns mit der Frequenzplanung in Funknetzen1. Abbildung 1 zeigt die Lage von Funkmasten in der Gemeinde Katlenburg-Lindau2. Eine Sendestation überdeckt dabei ein bestimmtes geographisches Gebiet. Dabei kann es vorkommen, dass bestimmte Bereiche von mehreren Sendestationen überdeckt werden. Diese Situation ist in Abbildung 2 dargestellt. Abbildung 1: Funkmasten Abbildung 2: Ausbreitungsbereiche der Sendestationen Wenn nun mehrere Sendestationen dieselbe Frequenz benutzen, sich aber ihre Sendebereiche überdecken, kann es zu Störungen kommen. Um diese Störungen zu vermeiden, sollten alle Sender, die ein gemeinsames Gebiet erreichen, unterschiedliche Sendefrequenzen verwenden. Wir modellieren dies Problem zunächst einmal, in dem wir uns auf die wichtigsten Informationen beschränken. Statt Funkmasten verwenden wir Knoten, die wir mit A – G beschriften (Abbildung 3). Überlappen sich zwei Sendebereiche, dann verbinden wir die zugehörigen Knoten mit einer Kante, wie in Abbildung 4 dargestellt. Abbildung 3: Funkmasten als Knoten Abbildung 4: Knoten und Kanten Da wir zur Lösung des Problems die genaue geographische Lage der Funkmasten nicht mehr benötigen, entsteht aus unseren Überlegungen folgender Modellgraph: 1 Die Idee stammt aus dem Buch „Graphentheorie. Eine anwendungsorientierte Einführung“ von Peter Tittmann, Fachbuchverlag Leipzig im Carl Hanser Verlag, 2003 2 Die Karte ist echt, die Funkmasten (glücklicherweise) nicht. Virtuelle Lehrerweiterbildung Informatik in Niedersachsen Kerstin Strecker Graphen – Teil 1 S. 3 Ein Graph besteht aus einer Menge von Knoten und einer Menge von Kanten. Kanten verbinden jeweils zwei Knoten miteinander. Um Störungen im Funknetz zu vermeiden, dürfen zwei Knoten, die über eine Kante miteinander verbunden sind, nicht in derselben Frequenz senden. Eine mögliche Lösung wäre, die Frequenz für einen Knoten festzulegen und dafür zu sorgen, dass alle Knoten, die mit diesem Knoten verbunden sind, auf einer anderen Frequenz senden. Dann betrachtet man die Nachbarknoten und sorgt dafür, dass ihre Nachbarn wiederum Abbildung 5: Modellgraph eine andere Frequenz nutzen. Nur muss man jetzt aufpassen, dass man die Überlegungen für den ersten Knoten damit nicht wieder verwirft. Das wird alles per Hand so unübersichtlich, dass man den Rechner zur Unterstützung braucht. Vielleicht fällt jedem jetzt schon ein passender Algorithmus zur Frequenzplanung ein, aber zunächst einmal muss geklärt werden, wie man diesen Graphen zur Verarbeitung mit dem Rechner darstellen kann. Grundsätzlich gibt es zwei Möglichkeiten. Man verwendet zur Darstellung eines Graphens entweder eine Adjazenzmatrix oder eine Adjazenzliste. 2. Darstellungen von Graphen 2.1 Adjanzenmatrix: Wir repräsentieren einen Graphen als zweidimensionale Reihung oder Matrix, deren Einträge angeben, ob eine Kante zwischen zwei Knoten existiert oder nicht: A B A false true C D E F G true false true false false B true false false true true false false C true false false false false false false D false true false false true true false E true true false true false false true F false false false true false false true G false false false false true true false Die Tabelle ist symmetrisch, was daran liegt, dass ein ungerichteter Graph zugrunde liegt. Wir kommen an späterer Stelle noch einmal darauf zurück. Weiterhin ist der Graph auch unbewertet, was sich ebenfalls in der Tabelle widerspiegelt, aber auch dazu später mehr. Kümmern wir uns zunächst um den entsprechenden Code, der diese Matrix implementiert. Unsere Knoten hatten die Bezeichnungen 'A' bis 'G'. Das kann in anderen Zusammenhängen anders sein, weshalb eine Klasse Knoten interessant ist, die hier für unsere Aufgabe angepasst wird und nur Daten vom Typ char berücksichtigt. Eine Anpassung für andere Zusammenhänge ist ersichtlich. Virtuelle Lehrerweiterbildung Informatik in Niedersachsen Kerstin Strecker Graphen – Teil 1 S. 4 Bei der Erstellung eines neuen Knotens, wird die Bezeichnung übergeben. Neue Knoten kann man in anderen Klassen erstellen mit dem Befehl: Wir verwenden diese Klasse nun, um in der Klasse Graph eine Knotenliste zu erstellen, die die Knoten entsprechend der Reihenfolge in der sie eingefügt werden enthält3. Bei der Erstellung eines neuen Graphen muss die Gesamtgröße (hier: 7) angegeben werden. Weiterhin wird die Adjazenzmatrix mit dem Wert false initialisiert. Das Attribut anzahl gibt den Index des ersten freien Platzes in der Knotenliste an, bzw. die Anzahl bereits eingefügter Knoten. Wird ein Knoten eingefügt, ist der Rückgabewert nur dann true, wenn das Einfügen erfolgreich war. Ein Knoten kann nur eingefügt werden, wenn die Anzahl der bereits vorhandenen Knoten kleiner ist als die maximale Anzahl. Eine Kante kann nur eingefügt werden, wenn die zugehörigen Knoten existieren. Es wird zunächst überprüft an welcher Stelle der Knotenliste diese Knoten sind, also welchen Index sie haben. Dann werden die entsprechenden Einträge der Adjazenzmatrix auf true gesetzt. Bei ungerichteten Graphen ist eine Kante von A nach B, gleichzeitig auch eine Kante von B nach A. Für ein Knoten wird in der Reihung knotenliste gesucht, an welcher Stelle (Index) der Knoten zu finden ist. Eine Kante wird entfernt, indem die entsprechenden Einträge in der Adjazenzmatrix wieder auf false gesetzt werden. 3 Die Implementierungsidee findet sich in ähnlicher Form auch im Lehrbuch „Informatik 4. Rekursive Datenstrukturen. Softwaretechnik“, Ernst Klett Verlag, 2009. Teile sind daraus entnommen. Virtuelle Lehrerweiterbildung Informatik in Niedersachsen Kerstin Strecker Graphen – Teil 1 S. 5 Um unseren Beispielgraphen einzugeben, benötigen wir folgende Aufrufe: Jetzt sind wir in der Lage, einen Algorithmus zur Frequenzplanung in diesem Funknetz zu entwickeln und vor allem zu implementieren. Doch zuvor noch eine weitere Darstellung von Graphen zum Zweck einer Implementation. Die meisten Einträge in der Adjazenzmatrix sind false. Für Kanten, die gar nicht vorhanden sind, sollte kein Speicherplatz unnötig allokiert werden. Besser ist hier eine dynamische Datenstruktur, die dynamisch mit der Anzahl der Kanten wachsen oder schrumpfen kann. Als Beispiel einer dynamischen Datenstruktur ist aus anderen Zusammenhängen der Datentyp „Liste“ sicher bekannt. Adjazenzlisten, als Repräsentanten von Graphen können diese Datenstruktur sinnvoll nutzen. 2.2. Adjazenzliste: Die Knoten des Graphen werden in einer Liste gespeichert. Zu jedem Knoten wird eine weitere Liste angelegt, in der die Nachbarn des Knoten gespeichert werden. Für unseren Graphen ergibt sich folgende Adjazenzlistendarstellung: Wobei grünlich unterlegt die Liste der Knoten des Graphen dargestellt ist und jeweils lila unterlegt die Listen der Nachbarn. Die unterschiedliche Länge der Nachbarnlisten visualisiert gut die Vorteile dynamischer Datenstrukturen. Für den Java-Code bedienen wir uns der Java-Klasse Vector, die eine Implementierung des ADT Liste darstellt. Zunächst müssen wir die Klasse Knoten um eine Liste mit Nachbarknoten erweitern: Die Nachbarliste besteht ebenfalls aus Knoten. Ansonsten bleibt die Klasse Knoten unverändert. Virtuelle Lehrerweiterbildung Informatik in Niedersachsen Kerstin Strecker Graphen – Teil 1 S. 6 Die Knotenliste ist jetzt keine Reihung mehr, deren Größe anfangs festgelegt werden muss, sondern eine Liste. Die Signatur der Methoden knotenEinfügen, kanteEinfügen und kanteEntfernen bleibt erhalten. Eine Kante „von“ Knoten „bis“ Knoten bedeutet, dass der Knoten „bis“ als Nachbar in die Nachbarliste des Knotens „von“ eingefügt werden muss und der Knoten „von“ als Nachbar des Knotens „bis“. Gerichtete Graphen müssen an dieser Stelle später modifiziert werden. Die Methoden add und remove der Klasse Vector vereinfachen die Methodenstruktur in der Klasse Graph. Nachdem die Klasse Graph nun auf die ein oder andere Weise implementiert ist, können wir uns um die Algorithmen kümmern, die auf Graphen operieren. Algorithmen auf Graphen sind deshalb interessant, weil viele Problemstellungen mit Hilfe von Graphen modelliert werden können. Im ersten Beispiel ging es um die Frequenzplanung bei Sendestationen. Um Störungen zu verhindern, sollten benachbarte Sendestationen nicht in derselben Frequenz senden, sofern sich ihre Sendebereiche überlappen. Es bleibt jedem selbst überlassen, hier einen funktionierenden Algorithmus anzugeben. Allerdings müssen die Klasse Knoten und die Klasse Graph ein bisschen der Problemstellung angepasst werden. Hilfreich für jede Art von Lösungsalgorithmus sind zusätzliche Informationen an den Knoten, eine Markierung beispielsweise, die anzeigt ob ein Knoten schon bearbeitet wurde oder ein Attribut, welches den gewählten Frequenzbereich für einen Knoten aufnehmen kann. Diese Klassenerweiterungen der Klassen Knoten und Graph sind in allen Algorithmen für Graphen notwendig, ihre Umsetzung dürfte jedoch bekannt sein, sie ist zumindest in diesem Rahmen nicht Gegenstand der Betrachtung. Virtuelle Lehrerweiterbildung Informatik in Niedersachsen Kerstin Strecker Graphen – Teil 1 S. 7 3. Ein bisschen Graphentheorie Ein sehr bekannter Graph ist zweifelsohne das „Haus vom Nikolaus“. Ein ganzes Buch widmet sich diesem Graphen4. Abbildung 6: Das Haus vom Nikolaus Das linke der Häuser in Abbildung 6 zeigt einen Graphen, wie wir ihn aus dem ersten Kapitel kennen mit Knoten und Kanten. Verbindet man nun nacheinander die Knoten A,B,C,D,B,E,A,D,E, so kann man das Haus „zeichnen“ ohne eine Kante zweimal zu „zeichnen“. Eine Folge von Kanten, die jeweils durch einen Knoten miteinander verbunden sind, nennt man Pfad. Oder formaler: Unter einem Pfad von Knoten a nach Knoten b der Länge n in einem Graphen versteht man eine Folge von n aufeinanderfolgenden Kanten. Ist der Pfad zusätzlich geschlossen, also Anfangs- und Endknoten identisch, spricht man von einem Zyklus. Im linken Graphen der Abbildung 6 finden sich die Zyklen A,B,E,A oder B, D, E, A, B und andere. Gibt es von jedem Knoten einen Pfad zu jedem anderen Knoten, wie das im linken Haus der Abbildung 6 der Fall ist, spricht man von einem zusammenhängenden Graphen, jeder Knoten ist von jedem anderen Knoten aus erreichbar. Das rechte Haus der Abbildung 6 stellt einen gerichteten Graphen dar. Es existiert eine Kante von A nach B aber keine Kante von B nach A. Hier muss bei einem Pfad zusätzlich die Richtung der Kanten berücksichtigt werden. Der Pfad A,D,C des linken Graphen ist kein Pfad im rechten Graphen. Der Pfad A,B,C,D,B,E,A,D,E ist allerdings auch hier vorhanden. Es ist leicht einzusehen, dass die Adjazenzmatrix eines gerichteten Graphen nicht mehr symmetrisch sein muss. Allerdings kann es natürlich Kanten von Knoten a nach Knoten b und von Knoten b nach Knoten a geben. Da die Adjazenzmatrix nicht mehr symmetrisch ist, muss der Code etwas abgewandelt werden und vor allem eine Vereinbarung getroffen werden, ob die Zeilen oder Spalten der Matrix den Startknoten der Kante angeben. Auch in gerichteten Graphen gibt es möglicherweise Zyklen. Bei gerichteten Graphen unterscheidet man zwischen stark zusammenhängenden Graphen, wenn es von jedem Knoten einen Weg zu jedem anderen Knoten gibt und schwach zusammenhängenden Graphen, wenn der Graph als ungerichteter Graph (bei Ignoranz der Richtung) zusammenhängend wäre. Der obige gerichtete Graph ist stark zusammenhängend. 4 Manfred Nitzsche: „Graphen für Einsteiger. Rund um das Haus vom Nikolaus“, Vieweg & Sohn Verlag, 2005 Virtuelle Lehrerweiterbildung Informatik in Niedersachsen Kerstin Strecker Graphen – Teil 1 S. 8 3.1. Eulersche Graphen Eulersche Graphen sind Graphen, in denen man eine eulersche Tour finden kann, einen ganz besonderen Pfad, der die folgenden Eigenschaften erfüllt: • der Pfad enthält keine Kante doppelt • der Pfad enthält sämtliche Kanten des Graphen • Anfangs- und Endknoten stimmen überein. Egal wie man das Haus vom Nikolaus auch zeichnet. Wir können die ersten beiden Anforderungen erfüllen, denn das sind auch die Voraussetzungen für das Haus vom Nikolaus. Aber so sehr wir uns bemühen, Punkt drei könnten wir nur erfüllen, wenn zwischen den Knoten A und E eine weitere Kante eingezeichnet wäre. Das Haus vom Nikolaus ist also (egal ob ungerichtet oder nicht) kein eulerscher Graph. Man kann sogar mathematisch beweisen, dass ein zusammenhängender Graph genau dann eulersch ist, wenn der Grad jedes Knotens gerade ist. (Der Grad eines Knotens ist dabei die Anzahl der Kanten, die von diesem Knoten ausgehen). 3.2. Hamiltonsche Graphen Ein Hamiltonscher Graph enthält einen hamiltonschen Kreis. Dies ist ein geschlossener Pfad, der jeden Knoten des Graphen genau einmal enthält. Ein hamiltonscher Kreis geht also durch jeden Knoten, muss aber nicht jede Kante des Graphen enthalten. Im Haus vom Nikolaus finden wir schnell den Pfad A,B,C,D,E,A, der ein hamiltonscher Kreis ist und damit ist das Haus vom Nikolaus ein hamiltonscher Graph. Wichtig ist, dass zwar Anfangs- und Endknoten gleich sind und damit doppelt genannt werden, ansonsten aber jeder Knoten nur genau einmal vorkommen darf und auch der Anfangs- und Endknoten zwischendurch kein weiteres Mal genannt werden darf. Hamiltonsche Kreise werden uns später wieder begegnen. Damit sei der Graphentheorie jetzt aber erst einmal genüge getan. Virtuelle Lehrerweiterbildung Informatik in Niedersachsen Kerstin Strecker Graphen – Teil 1 S. 9 4. Gewichtete Graphen und Dijkstra Neben der Frequenzplanung in Funknetzen gibt es eine klassische Anwendung gewichteter Graphen bei der Suche nach kürzesten Verbindungen zwischen zwei Orten. Folgende Abbildungen stammen aus dem Buch „Abenteuer Informatik“ von Jens Gallenbacher5. Unter der Fragestellung: „Wie funktioniert ein Routenplaner?“ wird folgende Straßenkarte mit Hilfe eines gewichteten Graphen modelliert: Zeichnung 1: Straßenkarte Zeichnung 2: Graph Die Ortschaften wurden dabei als Knoten dargestellt und mit ihrem Anfangsbuchstaben beschriftet. An den Kanten (Straßen) stehen nun Gewichte, die in diesem Fall die Entfernungen zwischen den Orten in km angeben. Es ist ersichtlich, dass die Adjazenzmatrix diese Gewichte in irgendeiner Form aufnehmen muss. Eine Möglichkeit ist die, eine zweidimensionale Reihung von int-Werten zu verwenden. Ein Zelleneintrag ist dabei die Entfernung zwischen zwei Knoten oder 0, wenn keine Kante zwischen diesen Knoten existiert. Bei der Verwendung von Adjazenzlisten müssen in die Liste der Nachbarknoten noch Informationen über die Entfernung hinzugefügt werden. Dies kann man realisieren, indem man die Klasse Knoten modifiziert und das Aufnehmen von Entfernungen ermöglicht. Alternativ können die Gewichte an den Kanten natürlich auch Fahrtzeiten zwischen den Orten sein oder ähnliches. Jetzt möchte ein Reisender von Imstadt (I) nach Oppenheim (O) und zwar auf dem kürzesten Weg. Denkt man zunächst, dass die direkte Verbindung über Pappstadt (P) mit 146 km günstig ist, kann man nach gründlichem Hinsehen eine Route mit nur 123 km über Pappstadt (P), Krupsing (K) und Flughafen (F) erkennen. Ein Algorithmus, der ausgehend von einem Startknoten die kürzesten Wege zu allen anderen Knoten im Graphen berechnet, ist der Dijkstra-Algorithmus6, der im Folgenden vorgestellt werden soll. Um ein Beispiel gründlicher zu bearbeiten mit einem bewerteten (gewichteten) und gerichteten Graphen, verwenden wir nicht die obige Karte, sondern folgendes Modell einer Straßenkarte mit vielen Einbahnstraßen ;-) und die entsprechende Adjazenzmatrix: 5 6 Jens Gallenbacher: „Abenteuer Informatik“, Spektrum Verlag, 2007 Benannt nach dem Erfinder Edsger Wybe Dijkstra (1930 – 2002) Virtuelle Lehrerweiterbildung Informatik in Niedersachsen Kerstin Strecker Graphen – Teil 1 A B C D E S. 10 F A B 10 C D 20 20 70 10 70 E Abbildung 7: gerichteter gewichteter Graph F 10 100 40 40 20 Freie Felder (keine Kanten) werden später mit 0 initialisiert. Eine Kante von A nach B bedeutet einen Zelleintrag in die Zelle[B][A]. Dies kann natürlich auch genau anders herum realisiert werden. Wir wählen als Startknoten Knoten A und suchen den kürzesten Weg zum Knoten F. Außerdem wollen wir die kürzeste Entfernung von A zu F berechnen. Dabei werden wir uns Stück für Stück vorarbeiten, ausgehend vom Startknoten zunächst die Nachbarknoten betrachten, die wiederum ihre Nachbarn betrachten usw. Auf dem Weg müssen wir einige Dinge zwischenspeichern. Das tun wir, indem die Knoten noch weitere Informationen aufnehmen. Ein Knoten muss als „bearbeitet“ markiert werden können, was wir dadurch realisieren, dass wir ihn anders farbig zeichnen. Außerdem muss er die Information über sein aktuelles Pfadgewicht (in rot) und seinen aktuellen Vorgängerknoten des bisherigen kürzesten Weges speichern können (in blau). Unter Pfadgewicht verstehen wir die Summe der Kantengewichte entlang eines Pfades. Aber das wird gleich alles noch verständlicher, keine Sorge. Außerdem brauchen wir eine Hilfsliste, die alle Knoten aufnimmt und stets automatisch nach der Größe der Pfadgewichte (aufsteigend) sortiert. Jetzt geht es los. Wir beginnen mit der Initialisierung des Graphen und der sortierten Hilfsliste: Knoten A B C D E F aktuelles Pfadgewicht 0 ∞ ∞ ∞ ∞ ∞ Vorgänger start null null null null null bearbeitet? nein nein nein nein nein nein Zunächst werden alle Referenzen auf die Vorgänger (außer beim Startknoten) auf null gesetzt und die Pfadgewichte auf ∞ . Wir suchen jetzt den ersten nicht bearbeiteten Knoten in der sortierten Liste. Nicht schwer, das ist Knoten A. Wir bearbeiten einen Knoten, indem wir für alle seine Nachfolgeknoten die Einträge in der Liste aktualisieren. Es ergibt sich folgendes Bild: (Die Liste wird automatisch entsprechend der Pfadgewichte sortiert.) Virtuelle Lehrerweiterbildung Informatik in Niedersachsen Kerstin Strecker Graphen – Teil 1 S. 11 Knoten A B D F C E aktuelles Pfadgewicht 0 10 20 100 ∞ ∞ Vorgänger start A A A null null bearbeitet? ja nein nein nein nein nein Der erste nicht bearbeitete Knoten der Hilfsliste ist jetzt Knoten B. Bearbeitet man Knoten B, werden die Einträge für die Nachbarknoten (hier: C und D) aktualisiert. Da wir aber kürzeste Wege suchen, aktualisieren wir Pfadgewicht und Vorgänger nur dann, wenn das Pfadgewicht dadurch echt kleiner wird. Diesmal ist das nicht der Fall, denn D hat ein aktuelles Pfadgewicht von 20 und ein Weg über B zu D würde ebenfalls 20 ergeben, also nicht echt kleiner sein. Es ergibt sich somit folgende Darstellung: Knoten A B D C F E aktuelles Pfadgewicht 0 10 20 30 100 ∞ Vorgänger start A A B A null bearbeitet? ja nein nein nein nein ja Das aktuelle Pfadgewicht bei Knoten C entspricht 30, weil das Pfadgewicht von Knoten B = 10 ist und Knoten B der Vorgänger von C ist. Außerdem beträgt das Kantengewicht von Knoten B nach Knoten C = 20 und da 20 + 10 = 30, ergibt sich für C ein Pfadgewicht von 30. Unser nächster Knoten in der Liste ist Knoten D. Wir erhalten: Knoten A B D C E F aktuelles Pfadgewicht 0 10 20 30 60 100 Vorgänger start A A B D A bearbeitet? ja ja nein nein nein ja Virtuelle Lehrerweiterbildung Informatik in Niedersachsen Kerstin Strecker Graphen – Teil 1 S. 12 Der Pfad nach C über Knoten D ist 90 km lang (20+70) und stellt keine Verbesserung dar, weshalb der Eintrag von C nicht aktualisiert wird. Doch bei der Bearbeitung von Knoten C passiert zum ersten Mal das, was man eine Relaxation nennt. Es findet tatsächlich eine Veränderung aktueller Pfadgewichte statt. Knoten A B D C E F aktuelles Pfadgewicht 0 10 20 30 40 70 Vorgänger start A A B C C bearbeitet? ja ja ja nein nein ja Das aktuelle Pfadgewicht von Knoten E und Knoten F wird nach unten korrigiert und entsprechend werden ihre Vorgängerknoten verändert. Warum ist das nun so? Knoten E hatte ein aktuelles Pfadgewicht von 60 bei einem Weg über den Vorgängerknoten D. Knoten C hat ein aktuelles Pfadgewicht von 30. Das Kantengewicht zwischen Knoten C und Knoten E ist 10. Da 30+10=40 und außerdem 40 < 60 ist, verläuft der bislang kürzeste Weg über Knoten C und ist 40 km lang. Entsprechendes gilt für F. Ein Weg über Knoten C hat ein Pfadgewicht von 30+40=70 und ist damit echt kleiner als 100, die Länge bei einem Weg über A. Im nächsten Schritt finden wir erneut eine Relaxation bei Knoten F: Knoten A B D C E F aktuelles Pfadgewicht 0 10 20 30 40 60 Vorgänger start A A B C E bearbeitet? ja ja ja ja nein ja Da Knoten F keine Nachfolgeknoten hat, sind wir mit der Bearbeitung am Ende. Der kürzeste Weg von Knoten A zu Knoten F ist 60 km lang. Den Wert können wir direkt aus der Liste entnehmen. Aber kann man auch rekonstruieren, wo der Weg entlang führt? Ja, man verfolgt einfach die Vorgängerknoten in der Liste zurück. Das Ziel war Knoten F. Für den kürzesten Weg muss der Vorgängerknoten E sein. Der Vorgänger von E ist nach der Liste C, dessen Vorgänger B und dessen Vorgänger A ist. Der Weg A,B,C,E,F stellt also die kürzeste Verbindung zwischen A und F dar. Bleibt nur noch eine letzte Aufgabe in diesem Kapitel: Die Implementation in Java. Virtuelle Lehrerweiterbildung Informatik in Niedersachsen Kerstin Strecker Graphen – Teil 1 S. 13 Als Vorüberlegung fassen wir den Dijkstra-Algorithmus zunächst in einem Struktogramm zusammen: Um das zu realisieren, passen wir zunächst einmal die Klasse Knoten etwas an: Zusätzlich zu einem Namen müssen die Knoten Informationen über das aktuelle Pfadgewicht, ihren Vorgänger und ihren Bearbeitungsstatus aufnehmen können. Für die Hilfsliste werden die Attribute entsprechend dem Beispiel auf unendlich bzw. null gesetzt. Mit dieser Methode kann ein Startknoten ausgewählt werden. set- und get-Methoden für das aktuelle Pfadgewicht. Außerdem set- und get-Methoden für den Bearbeitungsstatus und den Vorgänger, der ja bei der Relaxation verändert werden muss. Virtuelle Lehrerweiterbildung Informatik in Niedersachsen Kerstin Strecker Graphen – Teil 1 S. 14 Jetzt sehen wir uns ein Beispiel für eine Hilfsliste an, die Objekte der Klasse Knoten aufnehmen kann. Die Hilfsliste muss über eine Sortierfunktion verfügen, die die Knoten entsprechend ihres aktuellen Pfadgewichtes aufsteigend sortiert. Die Hilfsliste mit vorher festzulegender Größe nimmt Knoten der eben besprochenen Klasse auf. Mit der Methode werden. add können Knoten der Liste hinzugefügt Diese Methode sorgt dafür, dass die Liste sortiert wird, entsprechend den Pfadgewichten der einzelnen Knoten. Es wird ein einfacher SelectionSort-Algorithmus verwendet. Die Hilfsmethode swap vertauscht dabei zwei Elemente der Knotenliste. Für den Dijkstra-Algorithmus gibt diese Methode zurück, ob alle Knoten der Hilfsliste schon bearbeitet sind oder nicht. Diese Methode gibt den ersten unbearbeiteten Knoten der Hilfsliste zurück. Auch die Klasse Graph muss noch etwas verändert werden, bevor wir den Dijkstra-Algorithmus angeben können. Virtuelle Lehrerweiterbildung Informatik in Niedersachsen Kerstin Strecker Graphen – Teil 1 S. 15 Wie bereits angekündigt, verwenden wir eine Adjazenzmatrix, wobei der erste Index die Zeile und der zweite die Spalte angibt. Zunächst wird die Adjazenzmatrix mit 0en initialisiert. Diese Methode bleibt unverändert Da die Matrix nun nicht mehr symmetrisch ist (gerichteter Graph), muss man auf die Vereinbarung achten, wie Zeilen und Spalten gefüllt werden müssen. Auch diese Methode bleibt unverändert Die Klasse Graph bekommt eine Methode, die die Nachfolger zu jedem Knoten zurückgeben kann. Dazu wird für einen Knoten in der Adjazenzmatrix nachgesehen, welche Einträge ungleich 0 sind. Hier müssen Nachbarn zu finden sein. Die zurückzugebende Reihung wird um ein Element erweitert, nämlich dem gefundenen Nachfolger. Außerdem kann das Kantengewicht zwischen zwei Knoten zurück gegeben werden. Virtuelle Lehrerweiterbildung Informatik in Niedersachsen Kerstin Strecker Graphen – Teil 1 S. 16 Und hier ist er endlich: Der Dijkstra-Algorithmus in Java implementiert, gemäß des vorgegebenen Struktogramms: