Kap. 9: Graphtraversierung Ausgangspunkt oder Baustein fast jedes nichttrivialen Graphenalgorithmus 326 Graphtraversierung als Kantenklassifizierung forward s tree backward cross 327 Graphtraversierung als Kantenklassifizierung I Baumkanten: Elemente des Waldes, der bei der Suche gebaut wird I Vorwärtskanten: verlaufen parallel zu Wegen aus Baumkanten I Rückwärtskanten: verlaufen antiparallel zu Wegen aus Baumkanten I Querkanten: alle übrigen forward s tree backward cross 328 Breitensuche Baue Baum von Startknoten s, der alle von s erreichbaren Knoten mit möglichst kurzen Pfaden erreicht. Berechne Abstände: b s 0 e g c d f 1 2 tree backward cross forward 3 329 Breitensuche I Einfachste Form des Kürzeste-Wege-Problems I Umgebung eines Knotens definieren (ggf. begrenzte Suchtiefe) I Einfache, effiziente Graphtraversierung (auch wenn Reihenfolge egal) b s 0 e g c d f 1 2 tree backward cross forward 3 330 Breitensuche Algorithmenidee: Baum Schicht für Schicht aufbauen b s 0 e g c d f 1 2 tree backward cross forward 3 331 Function bfs(s) : Q:= hsi // aktuelle Schicht while Q 6= hi do exploriere Knoten in Q merke dir Knoten der nächsten Schicht in Q 0 Q:= Q 0 b s 0 e g c d f 1 2 tree backward cross forward 3 332 Repräsentation des Baums Feld parent speichert Vorgänger. I I noch nicht erreicht: parent[v ] = ? Startknoten/Wurzel: parent[s] = s b s e c d g tree parent f 333 Function bfs(s : NodeId) : (NodeArray of NodeId) ⇥(NodeArray of N0 [ {•}) d=h•, . . . , •i : NodeArray of N0 [ {•}; d[s]:= 0 parent=h?, . . . , ?i : NodeArray of NodeId; parent[s]:= s Q = hsi, Q 0 = hi : Set of NodeId // current, next layer for (`:= 0; Q 6= hi; `++ ) invariant Q contains all nodes with distance ` from s foreach u 2 Q do foreach (u, v ) 2 E do // scan u if parent(v ) = ? then // unexplored Q 0 := Q 0 [ {v } d[v ]:= ` + 1; parent(v ):= u (Q, Q 0 ):= (Q 0 , hi) // next layer return (parent, d) // BFS = {(v , w ) : w 2 V , v = parent(w )} 334 Repräsentation von Q und Q 0 mittels FIFO Q, Q 0 ! einzelne FIFO-Queue I Standardimplementierung in anderen Büchern + „Oberflächlich“ einfach Korrektheit mglw. weniger evident = Effizient (?) Übung! Übung: ausprobieren! 335 Alternative Repräsentation von Q und Q 0 I Zwei Stapel I Schleife 1⇥ ausrollen loop Q ! Q 0 ; Q 0 ! Q I Beide Stapel in ein Feld der Größe n Q ! Q0 336 Tiefensuche tree backward s cross forward b d e g f c 337 Tiefensuchschema für G = (V , E ) unmark all nodes; init foreach s 2 V do if s is not marked then mark s root(s) DFS(s, s) // make s a root and grow // a new DFS tree rooted at s Procedure DFS(u, v : NodeId) // Explore v coming from u foreach (v , w ) 2 E do if w is marked then traverseNonTreeEdge(v , w ) else traverseTreeEdge(v , w ) mark w DFS(v , w ) backtrack(u, v ) // return from v along the incoming edge 338 DFS-Baum init: root(s): traverseTreeEdge(v , w ): parent=h?, . . . , ?i : NodeArray of NodeId parent[s]:= s parent[w ]:= v tree s b parent mark s d root(s) dfs(s,s) traverseTreeEdge(s,b) mark b dfs(s,b) s b d e g f c e g f c 339 dfs(s,b) traverseTreeEdge(b,e) mark(e) dfs(b,e) traverseTreeEdge(e,g) mark(g) dfs(e,g) traverseNonTreeEdge(g,b) traverseTreeEdge(g,f) mark(f) dfs(g,f) backtrack(g,f) backtrack(e,g) traverseNonTreeEdge(e,f) traverseTreeEdge(e,c) mark(c) dfs(e,c) backtrack(e,c) backtrack(b,e) backtrack(s,b) s b e d s b b e b d g f c e d s f c d s g g f c e g f c 340 traverseTreeEdge(s,d) mark(d) dfs(s,d) traverseNonTreeEdge(d,e) traverseNonTreeEdge(d,f) backtrack(s,d) backtrack(s,s) s b e d s b d g f c e g f c 341 DFS-Nummerierung init: root(s): traverseTreeEdge(v , w ): dfsPos=1 : 1..n dfsNum[s]:= dfsPos++ dfsNum[w ]:= dfsPos++ u v :, dfsNum[u] < dfsNum[v ] . Beobachtung: Knoten auf dem Rekursionsstapel sind bzgl. 1 tree backward s cross forward 2 b 3 e sortiert 4 g d c 7 6 5 f 342 Fertigstellungszeit init: backtrack(u, v ): finishingTime=1 : 1..n finishTime[v ]:= finishingTime++ 7 tree backward s cross forward 5 b 4 e 2 g d c 6 3 1 f 343 Kantenklassifizierung bei DFS type (v , w ) tree forward backward cross dfsNum[v ] < dfsNum[w ] yes yes no no finishTime[w ] < finishTime[v ] yes yes no yes w is marked no yes yes yes forward s tree backward cross 344 Topologische Sortierung Definition 5 Eine lineare Anordnung t der Knoten eines DAGs G = (V , E ), in der alle Kanten von “kleineren” zu “größeren” Knoten verlaufen, heißt topologische Sortierung, d. h. 8(u, v ) 2 E : t(u) < t(v ). Beispiel: topologisch sortierter Kleidergraph, Quelle: Wikipedia Kleidergraph, Quelle: Wikipedia 345 Topologisches Sortieren mittels DFS Theorem 6 G ist kreisfrei (DAG) , DFS findet keine Rückwärtskante. In diesem Fall liefert t(v ):= n finishTime[v ] eine topologische Sortierung. 346 Topologisches Sortieren mittels DFS Theorem 6 G ist kreisfrei (DAG) , DFS findet keine Rückwärtskante. In diesem Fall liefert t(v ):= n finishTime[v ] eine topologische Sortierung. Beweis “)”: Annahme: 9 Rückwärtskante. Zusammen mit Baumkanten ergibt sich ein Kreis. Widerspruch. forward s tree backward cross 346 Topologisches Sortieren mittels DFS Satz: G kreisfrei (DAG) , DFS findet keine Rückwärtskante. In diesem Fall liefert t(v ):= n finishTime[v ] eine topologische Sortierung, d. h. 8(u, v ) 2 E : t(u) < t(v ). Beweis “(”: Keine Rückwärtskante Kantenklassifizierung z}|{ ) 8(v , w ) 2 E : finishTime[v ] > finishTime[w ] ) G ist kreisfrei und finishTime definiert umgekehrte topologische Sortierung. 347 Starke Zusammenhangskomponenten ⇤ Betrachte die Relation $ mit ⇤ u $ v falls 9 Pfad hu, . . . , v i und 9 Pfad hv , . . . , ui. ⇤ Beobachtung: $ ist Äquivalenzrelation ⇤ Die Äquivalenzklassen von $ bezeichnet man als starke Zusammenhangskomponenten. Übung DFS-basierter Linearzeitalgorithmus ! Algorithmen II 348 Mehr DFS-basierte Linearzeitalgorithmen I 2-zusammenhängende Komponenten: bei Entfernen eines Knotens aus einer Komponente bleibt diese zusammenhängend (ungerichtet) I 3-zusammenhängende Komponenten I Planaritätstest (lässt sich der Graph kreuzungsfrei zeichnen?) I Einbettung planarer Graphen 349 BFS ! DFS pro BFS: I nichtrekursiv I keine Vorwärtskanten I kürzeste Wege, „Umgebung“ pro DFS: I keine explizite Datenstruktur (Rekursionsstapel) für ToDos, daher mglw. einfacher I Grundlage vieler Algorithmen forward s tree backward cross 350 Kap. 10: Kürzeste Wege Eingabe: I Graph G = (V , E ) mit I I Kostenfunktion/Kantengewicht c : E ! R sowie Startknoten s. 3.0 km Ausgabe: für alle v 2 V : I Länge µ(v ) des kürzesten Pfades von s nach v , I µ(v ) := min {c(p) : p ist Pfad von s nach v } mit c(he1 , . . . , ek i) := Âki=1 c(ei ). 351 Kap. 10: Kürzeste Wege Eingabe: I Graph G = (V , E ) mit I I Kostenfunktion/Kantengewicht c : E ! R sowie Startknoten s. 3.0 km Ausgabe: für alle v 2 V : I Länge µ(v ) des kürzesten Pfades von s nach v , I µ(v ) := min {c(p) : p ist Pfad von s nach v } mit c(he1 , . . . , ek i) := Âki=1 c(ei ). Oft wollen wir auch „geeignete“ Repräsentation der kürzesten Pfade. 351 Anwendungen I Routenplanung I I I I Straßennetze Spiele Kommunikationsnetze Unterprogramm I I Flüsse in Netzwerken ... I Tippfehlerkorrektur I Disk Scheduling I ... 3.0 km 352 Grundlagen Gibt es immer einen kürzesten Pfad? Es kann negative Kreise geben! q v s p q v ... s p (2) u C uC weitere Grundlagen just in time 353 Azyklische Graphen später 354 Kantengewichte 0 Alle Gewichte gleich: Breitensuche (BFS)! b s 0 e g c d f 1 2 tree backward cross forward 3 355 Dijkstras Algorithmus Nun: Beliebige nichtnegative Kantengewichte M 0 Distance to M R 5 Lösung ohne Rechner: I I I I Kanten ! Fäden L O Kantengewicht ! Fadenlänge Knoten ! Knoten Dann: Am Startknoten anheben. 11 13 15 Q H G N F K P E C 17 17 18 19 20 S V J W 356 Korrektheit der Bindfäden M 0 Distance to M R Betrachte beliebigen Knoten v mit Hängetiefe d[v ]. L 9 Pfad mit Hängetiefe: verfolge straffe Fäden ¬9 kürzerer Pfad: falls es einen solchen Pfad gäbe, wäre einer seiner Fäden zerrissen! 5 11 13 15 O Q H G N F K P E C 17 17 18 19 20 S V J W 357 Edsger Wybe Dijkstra 1930–2002 I 1972 ACM Turing Award I THE: das erste Mulitasking-OS I Semaphor I Selbst-stabilisierende Systeme I GOTO Statement Considered Harmful Bildquelle: Wikipedia 358 Allgemeine Definitionen Wie bei BFS benutzen wir zwei Knotenarrays: Initialisierung: d[s] = 0, parent[s] = s d[v ] = •, parent[v ] = ? parent parent parent[v ] = Vorgänger von v auf dem (vorläufigen) kürzesten Pfad von s nach v Invariante: dieser Pfad bezeugt d[v ] d[v] parent I Kante d[v ] = aktuelle (vorläufige) Distanz von s nach v Invariante: d[v ] µ(v ) Kante Kante I s v 359 Kante (u, v ) relaxieren Falls d[u] + c(u, v ) < d[v ] (vielleicht d[v ] = •), setze I d[v ] := d[u] + c(u, v ) und I parent[v ] := u Invarianten bleiben erhalten! Beobachtung: d[v ] kann sich mehrmals ändern! 360 Dijkstras Algorithmus: Pseudocode initialize d, parent all nodes are non-scanned while 9 non-scanned node u with d[u] < • u := non-scanned node v with minimal d[v ] relax all edges (u, v ) out of u u is scanned now Behauptung: Am Ende definiert d die optimalen Entfernungen und parent die zugehörigen Wege 361 Beispiel 2 3 2 b c a 2 9 5 s 10 8 1 0 e f 4 d 0 7 10 2 3 5 7 2 a b c 2 9 5 s 10 8 1 0 e f 4 d 0 7 6 6 2 3 5 2 a b c 2 9 5 s 10 8 1 0 e f 4 d 0 7 10 7 2 3 5 2 a c b 2 9 5 s 10 8 1 0 f e 4 d 0 7 6 6 2 3 5 7 2 a c b 2 9 5 s 10 8 1 0 e f 4 d 0 10 6 7 2 3 5 7 2 a b c 2 9 5 s 10 8 1 0 f e 4 d 0 6 6 7 362