Gliederung 5. Compiler 1. 2. 3. 4. Struktur eines Compilers Syntaxanalyse durch rekursiven Abstieg Ausnahmebehandlung Arrays und Strings 6. Sortieren und Suchen 1. Grundlegende Datenstrukturen 2. Bäume 3. Hash-Verfahren (Streuspeicherung) 7. Graphen 1. Darstellung und Topologisches Sortieren 2. Kürzeste Wege 3. Fluß- und Zuordnungsprobleme Traversieren von Graphen (1) • Viele Algorithmen auf Graphen beruhen darauf, daß man alle Knoten (oder alle Kanten) des Graphen durchwandert: den Graphen traversiert. • Solche Traversierungen können ähnlich definiert werden, wie die entsprechenden Baumwanderungen. • Allerdings muss man bei Graphen darauf achten, dass man nicht in Endlosschleifen gerät, wenn der Graph Zyklen hat. • Aus diesem Grund markiert man bereits besuchte Knoten. • Dies ist eine Technik, die aus der Suche nach dem Ausweg aus einem Labyrinth bekannt ist (Tom Sawyer). • Ariadne benutzte ein Garnknäuel, um Theseus die Rückkehr aus dem Labyrinth zu ermöglichen, in dem er den Minotaurus getötet hatte. Traversieren von Graphen (2) Für die Traversierung betrachten wir die folgenden Strategien: Tiefendurchlauf (depth first) Breitendurchlauf (breadth first) • Der Tiefendurchlauf entspricht der PreorderBaumtraversierung, der Breitendurchlauf der LevelorderBaumtraversierung. • Alle folgenden Algorithmen können sowohl auf gerichtete als auch auf ungerichtete Graphen angewendet werden. • Bei Traversierungsalgorithmen ist es nicht sinnvoll, unzusammenhängende Graphen zu untersuchen. • Bei den folgenden Überlegungen setzen wir daher generell zusammenhängende Graphen voraus. Tiefendurchlauf (1) • Der Tiefendurchlauf lässt sich sehr einfach implementieren: Wir betrachten eine rekursive Implementierung. Damit werden die noch zu besuchenden Knoten im Keller verwaltet. • Der folgende Algorithmus Depth-First-Visit besucht alle Knoten, die von einem Ausgangsknoten k aus erreichbar sind, und markiert jeweils die besuchten Knoten. void Depth-First-Visit (int k ){ if (k ist noch nicht markiert){ Markiere (k); Besuche alle Nachbarn von k mit Depth-First-Visit; } } Tiefendurchlauf (2) • Wir benutzen ein statisches Feld für die Markierungen: private static boolean [] besucht; • Zunächst benötigen wir eine Prozedur, die die eigentliche Tiefensuche startet. • Sie stellt ein Array für die Markierungen zur Verfügung. Die rekursive Tiefensuche beginnt: void DFVisit(int k){ besucht = new boolean [KnotenZahl]; RekDFVisit(k); } Tiefendurchlauf (3) • Die rekursive Prozedur rekDFVisit testet ob der Knoten k bereits markiert ist. • Falls das nicht der Fall ist, markiert sie den aktuellen Knoten und führt, ähnlich wie bei den Baumwanderungen, eine Besuchermethode aus. • Statt einer expliziten Besuchermethode haben wir allerdings nur eine einfache Ausgabeanweisung eingebaut. • Schließlich besucht rekDFVisit alle Nachbarn, zu denen es eine direkte Verbindung gibt: void rekDFVisit(int k){ int Max = KnotenZahl; if ( !besucht[k] ){ besucht[k] = true; System.out.println(Knoten[k]); Iterator iter = getEdges (k); while (iter.hasNext ()) { Edge v = (Edge) iter.next (); rekDFVisit(v.dest); } }} Tiefendurchlauf – Beispiel - 1 • Angewandt auf Beispiel 1 ergibt sich: Graph Graph1 = new Graph(); Graph1.DFVisit(0); Graph1.DFVisit(2); Graph1.DFVisit(7); Marburg Gießen Frankfurt Bonn Köln Mannheim Fulda Kassel Würzburg Fulda Frankfurt Bonn Köln Gießen Marburg Kassel Mannheim Würzburg Bonn Frankfurt Fulda Gießen Köln Marburg Kassel Würzburg Mannheim Tiefendurchlauf – Beispiel - 2 • Angewandt auf Beispiel 2 ergibt sich: Graph2.DFVisit(0); San Francisco San Rafael Richmond Oakland Hayward San Mateo Palo Alto Fremont San Jose Santa Clara Scotts Valley Santa Cruz Watsonville Half Moon Bay Pacifica Graph2.DFVisit(4); San Mateo San Francisco San Rafael Richmond Oakland Hayward Fremont Palo Alto Santa Clara San Jose Watsonville Santa Cruz Scotts Valley Half Moon Bay Pacifica Graph2.DFVisit(9); Santa Clara Palo Alto San Mateo San Francisco San Rafael Richmond Oakland Hayward Fremont San Jose Watsonville Santa Cruz Scotts Valley Half Moon Bay Pacifica Breitendurchlauf (1) • Ähnlich wie bei der Baum-Traversierung in Level-Order wird eine Warteschlange als Hilfsspeicher verwendet. void BFVisit(int k){ int Max = KnotenZahl; boolean [] besucht = new boolean [Max]; Queue q = new Queue(Max+5); try { q.enQueue(k); while(!q.istLeer()){ int l = q.deQueue(); if (!besucht[l] ){ besucht[l]= true; System.out.println(Knoten[l]); Iterator iter = getEdges (k); while (iter.hasNext ()) { Edge v = (Edge) iter.next(); q.enQueue(v.dest); } }}} // try while und if beendet catch(QueueFehler s){ System.out.println( "Fehler bei der Breitensuche: "+s); }} Breitendurchlauf– Beispiel - 1 • Angewandt auf Beispiel 1 ergibt sich: Graph Graph1 = new Graph(); Graph1.BFVisit(0); Graph1.BFVisit(2); Graph1.BFVisit(7); Marburg Gießen Kassel Frankfurt Fulda Köln Bonn Mannheim Würzburg Fulda Frankfurt Gießen Kassel Würzburg Bonn Mannheim Köln Marburg Bonn Frankfurt Köln Mannheim Fulda Gießen Würzburg Kassel Marburg Breitendurchlauf– Beispiel - 2 • Angewandt auf Beispiel 2 ergibt sich: Graph2.BFVisit(0); San Francisco San Rafael Oakland San Mateo Pacifica Richmond Hayward Palo Alto Half Moon Bay Fremont Santa Clara Santa Cruz San Jose Scotts Valley Watsonville Graph2.BFVisit(4); San Mateo San Francisco Hayward Palo Alto Half Moon Bay San Rafael Oakland Pacifica Fremont Santa Clara Santa Cruz Richmond San Jose Scotts Valley Watsonville Graph2.BFVisit(9); Santa Clara Palo Alto San Jose Scotts Valley San Mateo Fremont Watsonville Santa Cruz San Francisco Hayward Half Moon Bay San Rafael Oakland Pacifica Richmond Transitive Hülle Eine zweistellige Relation R auf einer Menge V heißt transitiv, falls gilt : ∀ x, y, z ∈V : (x,y) ∈ R und (y,z) ∈ R → (x,z) ∈ R. Die transitive Hülle t(R) einer zweistelligen Relation R auf V ist die kleinste Relation Q, für die gilt: Q ist transitiv und R ⊆ Q. • Fasst man R als Graphen auf V auf, so gibt es einen Weg von x nach y genau dann, wenn es in t(R) eine Kante von x nach y gibt. • Man kann die Antwort auf die Frage „Gibt es in R einen Weg von x nach y?“ direkt aus der transitiven Hülle ablesen. • Es bleibt dann noch die Frage, wie man t(R) zu gegebenem R ermitteln kann. Transitive Hülle: Beispiel A D A D F F B B H C E Ausgangsgraph G G H C E transitive Hülle t(G) G Warshall's Algorithmus • Warshalls Algorithmus berechnet die transitive Hülle einer Relation, die durch eine Adjazenzmatrix boolean [][] A = { ... }; int dim = A.length; dargestellt ist. • A wird dabei schrittweise zur transitiven Hülle vergrößert: void WarshallAlg(){ int Max = dim; for (int y = 0; y < Max; y++) for (int x = 0; x < Max; x++) if (A[x][y]) for (int z = 0; z < Max; z++) if (A[y][z]) A[x][z] = true; } Frage: Funktioniert dieser Algorithmus auch, wenn man die x- und die y- Schleife vertauscht ??? Anmerkungen zu Warshall's Algorithmus • Wichtig ist bei Warshall's Algorithmus die Reihenfolge der Schleifen. Es spielen jeweils die Paare A[x,y] und A[y,z] eine Rolle. Von Bedeutung ist, dass die äußere Schleife über das mittlere Element verläuft. • Man erkennt unmittelbar, dass Warshalls Algorithmus nichts Falsches tut, da er A[x,z] nur dann auf true setzt, wenn bereits A[x,y] und A[y,z] true sind. • Dass er auch tatsächlich alle notwendigen Verbindungen hinzufügt, kann man durch Induktion über die äußere Schleife beweisen. Warshall: Beispiel 1 A B A B D C D C • Bei diesem Beispiel ist die Reihenfolge der Darstellung in der Matrix wichtig Ausgangsgraph G A B C D A 0 0 0 0 B 1 0 0 0 C 0 1 0 0 D 0 0 1 0 transitive Hülle t(G) A B C D A 0 0 0 0 B 1 0 0 0 C 1 1 0 0 D 1 1 1 0 X-Y-Pseudo-Hülle A B C D A 0 0 0 0 B 1 0 0 0 C 1 1 0 0 D 1 1 1 0 Warshall: Beispiel 2 A D A D B C E Ausgangsgraph G A B C D E A 0 0 1 1 0 B B 1 0 0 0 0 C 0 1 0 0 0 D 1 0 0 0 0 E 1 1 0 0 0 C E transitive Hülle t(G) A B C D E A 1 1 1 1 0 B 1 1 1 1 0 C 1 1 1 1 0 D 1 1 1 1 0 E 1 1 1 1 0 X-Y-Pseudo-Hülle A B C D E A 1 1 1 1 0 B 1 0 1 1 0 C 1 1 1 1 0 D 1 0 1 1 0 E 1 1 1 1 0 Korrektheit von Warshall's Algorithmus (1) • Dass Warshall's Algorithmus tatsächlich alle notwendigen Verbindungen hinzufügt, kann man durch Induktion über die äußere Schleife beweisen. • Als Induktionshypothese dient folgende Aussage P zu gegebenem y: • P(y) ≡ Gibt es zu beliebigen Knoten u und v einen Weg von u nach v, so dass alle Zwischenknoten aus der Menge M = {0, ...,y} sind, so wird in der y-ten Iteration A[u,v] = true gesetzt. Korrektheit von Warshall's Algorithmus (2) (1) y = 0 M = {0} Gibt es einen Pfad von u nach v mit allen Zwischenknoten in {0}, dann sind offensichtlich (u,0) und (v,0) in der Relation R enthalten! Innerhalb der Schleife wird dann das Tripel (x,y,z) mit x=u, y=0 und z=v im 0-ten Durchlauf bearbeitet und es wird (u,v) true gesetzt. (2) Die Behauptung P(y) gelte für 0 ... y. Es sei nun ein Weg von u nach v vorhanden, der y+1 benutzt. Dann gibt es einen der y+1 nur einmal benutzt. Auf Grund der Induktionshypothese wurde in einer früheren Iteration der Schleife bereits (u,y+1) und (y+1,v) eingefügt. In der (y+1)-ten Iteration wird daher (u,v) eingefügt. Also gilt auch P(y+1). u y+1 v Wenn P(y) für alle y gilt, was wir bewiesen haben, folgt daraus, dass der Algorithmus tatsächlich alle notwendigen Kanten einfügt! Wege in bewerteten Graphen • In einem bewerteten Graphen hatten wir bereits zu einem gegebenen Weg zwischen zwei Knoten u und v die Länge des Weges definiert: W = (u=k0 , k1, ..., kp=v) L(W) = Kantenwert(k0,k1) + ... + Kantenwert(kp-1,kp) • Für die Definition einer Entfernung ist diese Definition jedoch unbefriedigend, da wir verschiedene Wege mit gleichem Anfang und Ende haben können: Im Beispiel1 gilt: L(Marburg, Gießen, Frankfurt, Mannheim) = 184 und: L(Marburg, Gießen, Köln, Bonn, Mannheim) = 462 • Eine Entfernung für zwei Knoten u und v in einem bewerteten zusammenhängenden Graphen nehmen wir daher das Minimum der Längen aller Wege. Kürzeste Wege : Floyd • In einem zusammenhängenden bewerteten Graphen definieren wir die Entfernung dis(u,v) zwischen zwei Knoten u und v als das Minimum der Menge { L(W) : W ist ein Weg von u nach v} • Um die kürzeste Entfernung zwischen je zwei Knoten eines Graphen zu finden (engl.: all pairs shortest path problem), kann man Warshall's Algorithmus leicht verändern. • Dieser Algorithmus wird gewöhnlich R.W. Floyd zugeschrieben. • Anstelle der booleschen verwenden wir jetzt wieder eine bewertete Adjazenzmatrix A und setzen dabei jeweils statt A[x][z] = true A[x][z] = min(A[x][z] , A[x][y] + A[y][z] ) Die Komplexität von Floyd's Algorithmus ist: O (N3) Kürzeste Wege : alternativ • Floyds Algorithmus berechnet alle kürzesten Entfernungen in einem gegebenen Graphen. • Da man sich aber gewöhnlich nur für eine bestimmte kürzeste Entfernung interessiert (und nicht für alle), wollen wir hier einen Algorithmus diskutieren, der auf einer ähnlichen Idee aufbaut wie Warshalls Algorithmus, der aber den Vorteil hat, zu terminieren, sobald die kürzeste Entfernung zwischen zwei bestimmten Knoten gefunden wurde. • Dieser Algorithmus stammt von E.W.Dijkstra (1959). • Er berechnet für einen vorgegebenen Knoten u die kürzeste Entfernung zu allen anderen Knoten (engl.: single source shortest path problem). Dijkstra’s Algorithmus (1) Wir suchen die kürzeste Entfernung von einem Knoten u zu einem Knoten v. Grundidee des Algorithmus: • Dijkstra's Algorithmus färbt einen Graphen G schrittweise grün ein. • Ausgehend von der Menge S1 = {u} betrachten wir eine immer grösser werdende Menge der S1 "grünen Knoten". Für jeden Knoten k dieser Menge ist die kürzeste Entfernung von u nach k bekannt. • Die Menge der Knoten, die nicht in S1 sind, für die es aber eine direkte Verbindung, also eine Kante zu einem Knoten aus S1 gibt, nennen wir S2 : die "gelben Knoten". • Die Menge der restlichen Knoten des Graphen nennen wir S3 : die "roten Knoten". • Wir, beginnen mit S1 = {u} und S2 : Menge der Nachbarn von u. • In einem Erweiterungsschritt erweitern wir S1 um einen Knoten k aus S2 , entfernen k aus S2 und nehmen alle (neuen) Nachbarn von k zu S2 hinzu. Dijkstra’s Algorithmus (2) • Wir benutzen dabei folgende Definition der Nachbarn einer Menge S von Knoten: N(S) = {n | n ist mit mindestens einem s ∈ S direkt verbunden, aber nicht in S enthalten} S3 S2 S1 u G = S1 ∪ S 2 ∪ S 3 • Wir vergrößern die Menge der grünen Knoten um jeweils einen Knoten. • Wenn der Graph G aus n Knoten besteht, haben wir also spätestens nach n Schritten die kürzeste Entfernung von u nach v gefunden. • Leider ist in Dijkstra's Algorithmus die Auswahl des nächsten Knotens der grün wird nicht zielgerichtet, daher hoffen wir dass der Algorithmus früher terminiert – wieviel früher wissen wir aber nicht. Dijkstra’s Algorithmus (3) Beginn des Algorithmus: • Wir beginnen mit S1 = {u} und S2 = N(S1) . • Erweiterungsschritt: • Wir betrachten alle Paare (k1, k2) mit k1∈ S1 und k2∈ S2 • Zur Erweiterung wählen wir k = k2 aus einem Paar (k1, k2) für das der Ausdruck dis(u, k1) + Kantenwert(k1, k2) ein Minimum annimmt. • Dieser Wert ist die kürzeste Entfernung dis (u, k) ! (Dies wird auf der nächsten Folie noch bewiesen). • Wir können nunmehr S1 = S1 ∪ {k} und S2 = N(S2 - {k} ) nehmen. • Dijkstra's (Original-) Version des Algorithmus hat einen Aufwand von O(N2) im durchschnittlichen Fall. Dijkstra’s Algorithmus (4) • Wir wollen uns noch vergewissern, dass mit Hilfe der obigen Konstruktion auch tatsächlich der kürzeste Weg von u nach v gefunden wurde. Beweis: • Angenommen, es gäbe einen kürzeren Weg von u nach v, dann sei k3 der letzte Zwischenknoten dieses Weges, der noch aus S1 ist, und k4 sei sein Nachfolger. Also hat der Weg die Struktur u....k3k4.....v. S2 k4 k3 S1 u k1 k2 • Da der Gesamtweg nach k2 angeblich kürzer ist, ist auch der Weg von u nach k4 kürzer als der bisher gefundene Weg uK2 • Da aber die sonstigen Voraussetzungen der Konstruktion des Weges erfüllt sind, wäre das Paar (k3, k4) anstatt des Paares (k1, k2) von dem Algorithmus ausgewählt worden, womit wir einen Widerspruch herbeigeführt haben. K3 muss in liegen! Beispiel 1 Der kürzeste Weg von Bonn nach Kassel S1 {0} (1) {0} (2) {0, 5} (3) {0, 1, 5} (4) {0, 1, 3, 5} (5) {0, 1, 3, 5, 6} (6) {0, 1, 3, 5, 6, 7} (7) {0, 1, 2, 3, 5, 6, 7} (8) {0, 1, 2, 3, 5, 6, 7, 8} minimale Entfernung {0, 0, 0, 0, 0, 0, 0, 0, 0} {0, 0, 0, 0, 0, 0, 0, 0, 0} {0, 0, 0, 0, 0, 34, 0, 0, 0} {0,181, 0, 0, 0, 34, 0, 0, 0} {0,181, 0, 208, 0, 34, 0, 0, 0} {0,181, 0, 208, 0, 34, 224, 0, 0} {0,181, 0, 208, 0, 34, 224, 238, 0} {0,181, 285, 208, 0, 34, 224, 238, 0} {0,181, 285, 208, 0, 34, 224, 238, 317} k1 k2 min 0 0 5 0 3 1 1 7 0 5 1 3 6 7 2 8 4 0 34 181 208 224 238 285 317 342 Beispiel 2 (1) San Rafael 1 15 Richmond 2 18 San Francisco 15 12 0 3 Oakland 20 15 20 Pacifica 14 5 Hayward San Mateo 15 Half Moon Bay Auf dem Wege von San Rafael nach Scotts Valley 25 13 20 4 14 18 6 10 10 10 12 20 15 Palo Alto 50 7 Fremont 15 9 8 San Jose Santa Clara 35 Scotts Valley Santa Cruz 70 60 Watsonville 11 Beispiel 2 (2) San Rafael 1 15 Richmond 2 18 San Francisco 15 12 0 3 Oakland 20 15 20 Pacifica 14 5 Hayward San Mateo 15 Half Moon Bay Auf dem Wege von San Rafael nach Scotts Valley 25 13 20 4 14 18 6 10 10 10 12 20 15 Palo Alto 50 7 Fremont 15 9 8 San Jose Santa Clara 35 Scotts Valley Santa Cruz 70 60 Watsonville 11 Beispiel 2 (3) San Rafael 1 15 Richmond 2 18 San Francisco 15 12 0 3 Oakland 20 15 20 Pacifica 14 5 Hayward San Mateo 15 Half Moon Bay Auf dem Wege von San Rafael nach Scotts Valley 25 13 20 4 14 18 6 10 10 10 12 20 15 Palo Alto 50 7 Fremont 15 9 8 San Jose Santa Clara 35 Scotts Valley Santa Cruz 70 60 Watsonville 11 Beispiel 2 (4) San Rafael 1 15 Richmond 2 18 San Francisco 15 12 0 3 Oakland 20 15 20 Pacifica 14 5 Hayward San Mateo 15 Half Moon Bay Auf dem Wege von San Rafael nach Scotts Valley 25 13 20 4 14 18 6 10 10 10 12 20 15 Palo Alto 50 7 Fremont 15 9 8 San Jose Santa Clara 35 Scotts Valley Santa Cruz 70 60 Watsonville 11 Beispiel 2 (5) San Rafael 1 15 Richmond 2 18 San Francisco 15 12 0 3 Oakland 20 15 20 Pacifica 14 5 Hayward San Mateo 15 Half Moon Bay Auf dem Wege von San Rafael nach Scotts Valley 25 13 20 4 14 18 6 10 10 10 12 20 15 Palo Alto 50 7 Fremont 15 9 8 San Jose Santa Clara 35 Scotts Valley Santa Cruz 70 60 Watsonville 11 Beispiel 2 (6) San Rafael 1 15 Richmond 2 18 San Francisco 15 12 0 3 Oakland 20 15 20 Pacifica 14 5 Hayward San Mateo 15 Half Moon Bay Auf dem Wege von San Rafael nach Scotts Valley 25 13 20 4 14 18 6 10 10 10 12 20 15 Palo Alto 50 7 Fremont 15 9 8 San Jose Santa Clara 35 Scotts Valley Santa Cruz 70 60 Watsonville 11 Beispiel 2 (7) San Rafael 1 15 Richmond 2 18 San Francisco 15 12 0 3 Oakland 20 15 20 Pacifica 14 5 Hayward San Mateo 15 Half Moon Bay Auf dem Wege von San Rafael nach Scotts Valley 25 13 20 4 14 18 6 10 10 10 12 20 15 Palo Alto 50 7 Fremont 15 9 8 San Jose Santa Clara 35 Scotts Valley Santa Cruz 70 60 Watsonville 11 Beispiel 2 (8) San Rafael 1 15 Richmond 2 18 San Francisco 15 12 0 3 Oakland 20 15 20 Pacifica 14 5 Hayward San Mateo 15 Half Moon Bay Auf dem Wege von San Rafael nach Scotts Valley 25 13 20 4 14 18 6 10 10 10 12 20 15 Palo Alto 50 7 Fremont 15 9 8 San Jose Santa Clara 35 Scotts Valley Santa Cruz 70 60 Watsonville 11 Beispiel 2 (9) San Rafael 1 15 Richmond 2 18 San Francisco 15 12 0 3 Oakland 20 15 20 Pacifica 14 5 Hayward San Mateo 15 Half Moon Bay Auf dem Wege von San Rafael nach Scotts Valley 25 13 20 4 14 18 6 10 10 10 12 20 15 Palo Alto 50 7 Fremont 15 9 8 San Jose Santa Clara 35 Scotts Valley Santa Cruz 70 60 Watsonville 11 Beispiel 2 (10) San Rafael 1 15 Richmond 2 18 San Francisco 15 12 0 3 Oakland 20 15 20 Pacifica 14 5 Hayward San Mateo 15 Half Moon Bay Auf dem Wege von San Rafael nach Scotts Valley 25 13 20 4 14 18 6 10 10 10 12 20 15 Palo Alto 50 7 Fremont 15 9 8 San Jose Santa Clara 35 Scotts Valley Santa Cruz 70 60 Watsonville 11 Beispiel 2 (11) San Rafael 1 15 Richmond 2 18 San Francisco 15 12 0 3 Oakland 20 15 20 Pacifica 14 5 Hayward San Mateo 15 Half Moon Bay Auf dem Wege von San Rafael nach Scotts Valley 25 13 20 4 14 18 6 10 10 10 12 20 15 Palo Alto 50 7 Fremont 15 9 8 San Jose Santa Clara 35 Scotts Valley Santa Cruz 70 60 Watsonville 11 Beispiel 2 (12) San Rafael 1 15 Richmond 2 18 San Francisco 15 12 0 3 Oakland 20 15 20 Pacifica 14 5 Hayward San Mateo 15 Half Moon Bay Auf dem Wege von San Rafael nach Scotts Valley 25 13 20 4 14 18 6 10 10 10 12 20 15 Palo Alto 50 7 Fremont 15 9 8 San Jose Santa Clara 35 Scotts Valley Santa Cruz 70 60 Watsonville 11 Beispiel 2 (13) San Rafael 1 15 Richmond 2 18 San Francisco 15 12 0 3 Oakland 20 15 20 Pacifica 14 5 Hayward San Mateo 15 Half Moon Bay Auf dem Wege von San Rafael nach Scotts Valley 25 13 20 4 14 18 6 10 10 10 12 20 15 Palo Alto 50 7 Fremont 15 9 8 San Jose Santa Clara 35 Scotts Valley Santa Cruz 70 60 Watsonville 11 Beispiel 2 (14) San Rafael 1 15 Richmond 2 18 San Francisco 15 12 0 3 Oakland 20 15 20 Pacifica 14 5 Hayward San Mateo 15 Half Moon Bay Auf dem Wege von San Rafael nach Scotts Valley 25 13 20 4 14 18 6 10 10 10 12 20 15 Palo Alto 50 7 Fremont 15 9 8 San Jose Santa Clara 35 Scotts Valley Santa Cruz 70 60 Watsonville 11 Beispiel 2 (15) San Rafael 1 15 Richmond 2 18 San Francisco 15 12 0 3 Oakland 20 15 20 Pacifica 14 5 Hayward San Mateo 15 Half Moon Bay Auf dem Wege von San Rafael nach Scotts Valley 25 13 20 4 14 18 6 10 10 10 12 20 15 Palo Alto 50 7 Fremont 15 9 8 San Jose Santa Clara 35 Scotts Valley Santa Cruz 70 60 Watsonville 11 Kürzeste Wege als Programm (1) • Wir vereinfachen den Algorithmus wie folgt: • Wir nehmen als Menge S2 stets die ganze Restmenge S3. • Dadurch steigt der Aufwand zwar wieder auf O(N3) , aber das Programm wird einfacher. void minSuche(int u, int v){ int Max = KnotenZahl; boolean [] S = new boolean[Max]; S[u] = true; int [] MinEntf = new int [Max]; ... Kürzeste Wege als Programm (2) ... while (!S[v]){ int MinMin = 1000000; int minind = 0; for (int k1 = 0; k1 < Max; k1++) for (int k2 = 0; k2 < Max; k2++) if (S[k1] && !S[k2] && (Kanten[k1][k2] > 0)){ int MinNeu = Kanten[k1][k2] + MinEntf[k1]; if (MinNeu < MinMin){ MinMin = MinNeu; minind = k2; } } S[minind] = true; MinEntf[minind] = MinMin; System.out.println("nach "+ Knoten[minind] + " ist " + MinMin + " km."); } } Kürzeste Wege – Beispiel - 2 Min-Suche Ausgangsknoten: San Rafael Zielknoten: Scotts Valley nach Richmond ist 15 km. nach San Francisco ist 18 km. nach Oakland ist 30 km. nach Pacifica ist 33 km. nach San Mateo ist 38 km. nach Half Moon Bay ist 48 km. nach Hayward ist 50 km. nach Palo Alto ist 56 km. nach Fremont ist 64 km. nach Santa Clara ist 66 km. nach San Jose ist 81 km. nach Santa Cruz ist 98 km. nach Scotts Valley ist 101 km. Kürzester Weg und aufspannender Baum • Ist v der letzte (d.h. von u am weitesten entfernte) Knoten des Graphen G, wie im folgenden Beispiel, so liefert Dijkstra's Algorithmus offenbar einen aufspannenden Baum für G - und zwar gerade denjenigen, der für jeden Knoten k den kürzesten Weg von u nach k darstellt. • Beispiel 1 Min-Suche Ausgangsknoten: Bonn Zielknoten: Kassel nach Köln ist 34 km. nach Frankfurt ist 181 km. nach Gießen ist 208 km. nach Mannheim ist 224 km. nach Marburg ist 238 km. nach Fulda ist 285 km. nach Würzburg ist 317 km. nach Kassel ist 342 km. Kürzester Weg und aufspannender Baum: Beispiel 1 Köln 5 174 Marburg Bonn 224 106 3 Gießen 66 181 1 88 6 Mannheim 4 Kassel 96 7 30 34 0 104 2 Fulda 104 Frankfurt 136 93 8 Würzburg Literatur • Die Vorlesungsfolien wurden aus der Vorlesung „Informatik II a, SS 2002“ von Prof. Sommer (Uni-Marburg) übernommen