Kap. 6.3: Traversieren von Graphen Kap. 6.4: Elementare Graphalgorithmen Professor Dr. Petra Mutzel Lehrstuhl für Algorithm Engineering, LS11 Fakultät für Informatik, TU Dortmund 19. VO DAP2 SS 2008 Petra Mutzel 19. Juni 2008 DAP2 SS08 1 Anmeldung zur Klausur 25.07.2008 um 9:00 Uhr • Informatik AI/KI Bacc+Diplom: BOSS System • Informationstechnik Diplom: ACHTUNG: bis 20.06.08: online • Elektrotechnik Diplom: direkt bei uns • Physik Bacc/Diplom: ihr Dekanat • Datenanalyse und Datenmanagement / Statistik Bacc/Diplom: ihr eigenes Prüfungsamt • Mathematik Bacc/Diplom: erst ab 1.7. bekannt (wahrscheinlich BOSS System) • Lehramt, BaMaLa: direkt bei uns? Petra Mutzel DAP2 SS08 2 Nachtest für Ausnahmefälle • Di 1. Juli 2008, 10:15 Uhr, OH14, R. 202 • Anmeldung bis 26. Juni erforderlich via Email an Carsten Gutwenger • Stoff: Alles bis inkl. Hashing (inkl.1.-10. Übungsblatt) • Teilnahmevoraussetzungen: • Attest/Entschuldigt beim 1. oder 2. Übungstest • oder besondere Härtefälle anerkannt in meiner Sprechstunde Di 24.6.08,14:15 Uhr-15:15 Uhr Petra Mutzel DAP2 SS08 3 Motivation Heute braucht es keine Motivation, denn: DFS Spielereien mit Petra Mutzel DAP2 SS08 4 Überblick • Darstellung von Graphen im Rechner • Traversieren von Graphen: – Breitensuche (BFS) – Tiefensuche (DFS) • Elementare Graphalgorithmen: – Zusammenhangskomponenten – Kreise in Graphen Petra Mutzel DAP2 SS08 5 Darstellung von Graphen im Rechner: Statische Graphen • Im Folgenden sei V={v1,v2,…,vn} • 1. Möglichkeit: Adjazenzlisten • Idee: Speichere für jeden Knoten seine Nachbarmenge in einer Liste • Realisierung: z.B. Knoten in Array und Nachbarkanten jedes Knotens als einfach verkettete Liste Petra Mutzel DAP2 SS08 6 Darstellung von Graphen im Rechner: Statische Graphen • 2. Möglichkeit: Adjazenzmatrix • Idee: Eine VxV Matrix enthält 0/1-Einträge für jedes Knotenpaar {u,v} • Realisierung: • Sei M=(mi,j) eine n x n Matrix mit mij:=1 falls (vi,vj)∈E, und mij:=0 sonst. • bei Mehrfachkanten schreibe statt 1 die Anzahl der Kanten Petra Mutzel DAP2 SS08 7 Darstellung gerichteter Graphen Adjazenzlisten Adjazenzmatrix Petra Mutzel DAP2 SS08 8 Darstellung ungerichteter Graphen Adjazenzlisten Adjazenzmatrix ist symmetrisch: nur Speicherung Petra Mutzel DAP2 Hälfte SS08 9 der oberen Diskussion HIER: ab jetzt Adjazenzlisten für dünne Graphen vorzuziehen! Adjazenzliste: • Speicherplatzverbrauch: linear: Θ(|V|+|E|) • Zeit für Aufbau: linear: Θ(|V|+|E|) • Abfrage, ob Kante (u,v) existiert: Θ(d(v)) • Iteration über alle Nachbarn von v∈V: Θ(d(v)) Adjazenzmatrix: • Speicherverbrauch immer quadratisch: Θ(|V|2) • Zeit für Aufbau: immer quadratisch: Θ(|V|2) • Abfrage, ob Kante (u,v) existiert: Θ(1) • Iteration über alle Nachbarn von v∈V: Θ(|V|) Petra Mutzel DAP2 SS08 10 10 Definitionen • Die Dichte (density) eines Graphen G ist das Verhältnis |E| / |V|. • G heißt dünn, falls seine Dichte O(1) ist • G heißt dicht, falls seine Dichte Ω(|V|) ist. Petra Mutzel DAP2 SS08 11 11 Darstellung von Graphen im Rechner: Dynamische Graphen • Dynamisch unter den Operationen: – Hinzufügen neuer Knoten und Kanten – Entfernen von Knoten und Kanten • Idee: für gerichtete Graphen: • Inzidenzlisten: speichere ein- und ausgehende Knoten bei v • Knoten in doppelt verketteter Liste (damit Entfernen in konstanter Zeit) • Inzidenzlisten in doppelt verketteten Listen Petra Mutzel DAP2 SS08 12 12 Realisierung Dynamische Graphen: Liste für die Knoten struct Node var Node prev var Node next var Edge outHead var Edge inHead var int index end struct // Vorgänger Knotenliste // Nachfolger in Knotenliste // Listenanfang ausgeh. Kanten // Listenanfang eingeh. Kanten // fortlaufender Index Petra Mutzel DAP2 SS08 13 13 Realisierung Dynamische Graphen: Liste für die Kanten struct Edge var Edge prevOut var Edge nextOut var Edge prevIn var Edge nextIn var node source var node target end struct //Vorgänger in Liste ausg. Kanten //Nachfolger in Liste ausg. Kanten // Vorgänger in Liste eing. Kanten // Nachfolger in Liste eing. Kanten // Anfangsknoten der Kante // Endknoten der Kante Achtung: jeder Kanteneintrag ist genau einmal in Liste enthalten Petra Mutzel DAP2 SS08 14 14 Darstellung von Graphen im Rechner: Dynamische Graphen Legende Liste der eingehenden Kanten: 15 Analyse Dynamischer Graphen • • • • • • • • • Speicherplatzverbrauch: linear: Θ(|V|+|E|) Zeit für Aufbau: linear: Θ(|V|+|E|) Abfrage, ob Kante (u,v) existiert: Θ(d(v)) Iteration über alle Nachbarn von v∈V: Θ(d(v)) Iteration über alle ausg. Kanten von v∈V: Θ(d-(v)) Iteration über alle eing. Kanten von v∈V: Θ(d+(v)) Einfügen eines Knotens bzw. Kante: Θ(1) Entfernen einer Kante: Θ(1) Entfernen eines Knotens: Θ(d(v)) Man geht davon aus, dass man jeweils Zeiger auf die Knoten und beim Entfernen auch auf die Kanten gegeben hat 16 Kap. 6.3 Traversieren von Graphen Traversieren: systematisches Durchwandern von Graphen HIER: ungerichtete Graphen Def.: Der graphentheoretische Abstand zweier Knoten u, v eines ungerichteten Graphen G ist die Länge des kürzesten Weges von u nach v, falls ein solcher existiert, sonst ∞. Achtung: hierbei werden keine vorgegebenen Kantenlängen bzw. Kantengewichte berücksichtigt. Kap. 6.3.1 Breitensuche (BFS) engl.: Breadth-first-search, BFS Idee: besuche die Knoten nach aufsteigendem graphentheoretischen Abstand zu einem vorher festgelegten Startknoten. 1. Fall: Wir sehen v zum ersten Mal (von u aus): • Dann muss dist(v) um genau 1 größer sein als dist(u). • Wir können v besuchen, nachdem wir alle bisher gesehenen Knoten besucht haben. 2. Fall: wir haben v schon gesehen → nichts zu tun Petra Mutzel DAP2 SS08 18 18 Breitensuche (BFS) Methode: (1) Starte am Knoten u:=s. Sei Q:=∅ eine Queue. (2) Für alle Knoten v∈N(u) // erforsche Knoten u (3) Falls wir v zum ersten Mal sehen: (4) markiere v als „gesehen“ (5) dist(v):=dist(u)+1; merke Vorgänger; (6) hänge v hinten an Q an. (7) Sei u der nächste Knoten in Q. Gehe zu (2) dist(v) enthält den graphentheoretischen Abstand von u nach v; π(v) den Vorgänger des kürz. Weges Petra Mutzel DAP2 SS08 19 19 Beispiel für BFS 1 1 c b Petra Mutzel DAP2 SS08 20 20 Beispiel für BFS 1 1 2 2 c b e d Petra Mutzel DAP2 SS08 21 21 Beispiel für BFS 1 1 2 2 b e d Petra Mutzel DAP2 SS08 22 22 Beispiel für BFS 1 1 2 3 2 3 e d Petra Mutzel g f DAP2 SS08 23 23 Beispiel für BFS 1 1 2 3 2 3 d Petra Mutzel g f DAP2 SS08 24 24 Beispiel für BFS 1 1 2 3 2 3 g Petra Mutzel f DAP2 SS08 25 25 Beispiel für BFS 1 1 2 2 3 3 f Petra Mutzel DAP2 SS08 26 26 Beispiel für BFS Breitensuche entspricht einer level-order Traversierung in diesem BFS-Baum. 1 1 2 2 3 3 Die orangen Kanten (diejenigen Kanten (u,v), die zum ersten Mal v besuchen) bilden einen Baum: den BFS-Baum (oder BFS-Wald, wenn G nicht zusammenhängend ist) Algorithmus BFS(s) Sei G=(V,E) ungerichteter Graph, s,u,v: Knoten (1) for all v∈V \ {s} do { marked[v]:=0; dist[v]:=∞} (2) Q.put(s); marked[s]:=1; dist[s]:=π[s]:=0 (3) while not Q.ISEMPTY() do { (4) u:=Q.GET() (5) for all v∈N(u) do { (6) if not marked[v] then { (7) Q.Put(v) (8) marked[v]:=1 (9) dist[v]:=dist[u]+1; π[v]:=u (10) } } } Petra Mutzel DAP2 SS08 30 30 BFS-Baum • Im Feld π[v] werden die Vorgänger von v gespeichert. • Die Kanten (π(v),v) bilden einen Baum mit Wurzel s. • Die Höhe des BFS-Baumes mit Startknoten s ist eindeutig bestimmt. • Die Tiefe eines Blattes v entspricht dem graphentheoretischen Abstand von v zu s s 1 Wurzel 1 2 2 3 3 Petra Mutzel DAP2 SS08 31 31 Analyse von BFS • • • • Initialisierung: Θ(|V|) Die while-Schleife wird für jeden von s aus erreichbaren Knoten genau einmal durchlaufen, da jeder Knoten höchstens einmal in die Queue kommt. Die for all-Schleife durchläuft für jeden Knoten die Liste seiner Nachbarn N(v), das ist also jeweils Θ(d(v)) Aufwand Gesamtaufwand: Θ(|V|)+∑ Θ(d(v))= Θ(|V|+|E|), v∈V da im Worst Case alle Knoten von s aus erreichbar sein können Problem USSSP Unweighted Single-Source Shortest Path (USSSP): • Gegeben: ungerichteter Graph G=(V,E) • Gesucht: kürzester Weg von s zu jedem Knoten in G. Petra Mutzel DAP2 SS08 33 33 Breitensuche (BFS) Theorem: Sei G=(V,E) ein ungerichteter Graph und s∈V. Dann löst der Algorithmus BFS(s) das Unweighted Single-Source Shortest Path (USSSP) für Startknoten s in Zeit O(|V|+|E|). Petra Mutzel DAP2 SS08 34 34 Kap. 6.3.2 Tiefensuche Petra Mutzel DAP2 SS08 35 35 Tiefensuche (DFS) engl.: Depth-first-search, DFS Idee: besuche die Knoten rekursiv: wenn v zum ersten Mal gesehen wird, markiere ihn als gesehen und erforsche den Graphen von v aus weiter! • • Im Gegensatz zu Breitensuche, wird hier der Graph erst einmal in seiner Tiefe durchdrungen. Bei Breitensuche werden erst alle gesehenen Knoten bearbeitet, bevor die neuen bearbeitet werden (Queue). Hier wir immer erst das Neue erledigt. Petra Mutzel DAP2 SS08 36 36 Tiefensuche DFS Methode: DFS-VISIT(v): • Durchlaufe alle Nachbarn von v • Für jeden neu entdeckten (nicht markierten) Knoten w: – Markiere w; – rufe DFS-VISIT(w) auf. • Der folgende Algorithmus DFS(G) durchwandert alle Knoten des Graphen. • Im Feld π[w] wird jeweils der Vorgänger von w (also v) gespeichert. Petra Mutzel DAP2 SS08 37 37 Algorithmus DFS(G) Sei G=(V,E) ungerichteter Graph, v,w: Knoten (1) for all v∈V do { marked[v]:=0; π[v]:=nil} (2) for all v∈V do { (3) if not marked[v] then DFS-VISIT(v) (4) } (5) procedur DFS-VISIT(Node v) (6) marked[v]:=1 (7) for all w∈N(v) do (8) if not marked[w] then { (9) π[w]:=v (10) DFS-VISIT(w) (11) } Beispiel für DFS 2 c Petra Mutzel DAP2 SS08 39 39 Beispiel für DFS 2 3 e c Petra Mutzel DAP2 SS08 40 40 Beispiel für DFS g e c 2 3 Petra Mutzel 4 DAP2 SS08 41 41 Beispiel für DFS f g e c 2 3 4 5 Petra Mutzel DAP2 SS08 42 42 Beispiel für DFS f g e c 2 3 4 5 Petra Mutzel DAP2 SS08 43 43 Beispiel für DFS g e c 2 3 4 5 Petra Mutzel DAP2 SS08 44 44 Beispiel für DFS d e c 2 3 6 4 5 Petra Mutzel DAP2 SS08 45 45 Beispiel für DFS b d e c 2 7 3 6 4 5 Petra Mutzel DAP2 SS08 46 46 Beispiel für DFS b d e c 2 7 3 6 4 5 Petra Mutzel DAP2 SS08 47 47 Beispiel für DFS d e c 2 7 3 6 4 5 Petra Mutzel DAP2 SS08 48 48 Beispiel für DFS 2 e c 7 3 6 4 5 Petra Mutzel DAP2 SS08 49 49 Beispiel für DFS 2 c 7 3 6 4 5 Petra Mutzel DAP2 SS08 50 50 Beispiel für DFS 2 7 3 6 4 5 Petra Mutzel DAP2 SS08 51 51 Beispiel für DFS 2 7 6 3 4 5 Die orangen Kanten (diejenigen Kanten (u,v), die zum ersten Mal v besuchen) bilden einen Baum: den DFS-Baum (G zusammenhängend, sonst Wald) Sei G zusammenhängend DFS-Baum DFS-Baum besteht aus • Baumkanten (tree edges, T-Kanten): (π(v),v) • Rückwärtskanten (back edges, B-Kanten): s.u. • • DFS teilt die Kanten in T-Kanten und B-Kanten Für jede B-Kante (u,v) gilt: Es gibt genau einen Weg von v nach u mit T-Kanten im DFS-Baum 2 3 4 v 7 6 u 5 Sei G zusammenhängend DFS-Baum • • Jeder Knoten erhält eine eindeutige DFS-Nummer. Für die Rückwärtskanten (u,v) gilt: DFSNUM(u) > DFSNUM(v). • • DFS teilt die Kanten in T-Kanten und B-Kanten Für jede B-Kante (u,v) gilt: Es gibt genau einen Weg von v nach u mit T-Kanten im DFS-Baum 2 3 4 v 7 6 u 5 Analyse von DFS • • • • • Initialisierung: Θ(|V|) Die for-all-Schleife ohne Aufrufe von DFS-VISIT(): O(|V|). DFS-VISIT() wird für jeden Knoten genau einmal aufgerufen. Jeder Aufruf von DFS-VISIT(v) (ohne Kosten der rekursiven Aufrufe) benötigt Θ(d(v)) Zeit. Gesamtaufwand: Θ(|V|)+∑ Θ(d(v))= Θ(|V|+|E|) v∈V Petra Mutzel DAP2 SS08 57 57 BFS-Baum vs. DFS-Baum BFS-Baum 1 2 3 Höhe eindeutig 1 2 2 3 3 4 DFS-Baum 7 6 Petra Mutzel 5 DAP2 SS08 58 58 BFS & DFS BFS-Baum DFS-Baum Petra Mutzel DAP2 SS08 59 59 Kap. 6.4 Elementare Graphenalgorithmen Kap. 6.4.1 Komponenten eines Graphen Zusammenhangskomponenten Petra Mutzel DAP2 SS08 60 60 Definitionen (Zusammenhang) • G heißt zusammenhängend (connected), wenn |V|≧1 und für jedes Paar u,v von Knoten ein Weg von u nach v in G existiert. • Ein Graph G´=(V´,E´) mit V´⊆ V und E´⊆ E ist ein Untergraph (Teilgraph, subgraph) von G. Wir schreiben G´⊆G. • Eine (Zusammenhangs-) Komponente (component) von G ist ein maximaler zusammenhängender Untergraph U von G, d.h. es gibt keinen anderen zusammenhängenden Untergraphen U´ mit U⊆U´. Petra Mutzel DAP2 SS08 61 61 Komponenten eines Graphen C1 C3 C2 C4 T-Kanten des DFS-Waldes Petra Mutzel DAP2 SS08 62 62 Bestimmen der Zusammenhangskomponenten Idee: • Beginne an einem Knoten s und bestimme alle von s aus erreichbaren Knoten → Komponente 1 (Markierung mit 1) • Beginne nun bei einem noch unmarkierten Knoten v (marked==0) und bestimme alle von v aus erreichbaren Knoten; markiere dabei mit 2 → Komponente 2 • …u.s.w. bis alle Knoten markiert sind Petra Mutzel DAP2 SS08 63 63 Algorithmus COMPONENTS(G) Sei G=(V,E) ungerichteter Graph, v,w: Knoten (1) for all v∈V do { marked[v]:=0} (2) numComps:=0 (3) for all v∈V do { (4) if not marked[v] then { (5) numComps:=numComps+1 (6) DFS-COMP(v) (7) } } (8) Procedure DFS-COMP(Node v) (9) marked[v]:=numComps (10) for all w∈N(v) do (11) if not marked[w] then (12) DFS-COMP(w) Bestimmen der Zusammenhangskomponenten Theorem: Der Algorithmus COMPONENTS(G) bestimmt die Zusammenhangskomponenten eines Graphen G=(V,E) in Zeit Θ(|V|+|E|). Petra Mutzel DAP2 SS08 65 65 Kap. 6.4.2 Kreise in Graphen • Aufgabe: Gegeben ist ein ungerichteter Graph G=(V,E). Enthält G einen Kreis? • Idee: mittels DFS Petra Mutzel DAP2 SS08 66 66 Kreise und DFS • Lemma: Sei G ein ungerichteter Graph und F ein beliebiger DFS-Wald für G. Dann ist G genau dann kreisfrei, wenn G keine Rückwärtskante (B-Kante) bezüglich F enthält. Beweis: • 1. Fall: F enthält keine B-Kante: dann besteht F nur aus T-Kanten → F enthält alle Kanten aus G → G kreisfrei • 2. Fall: Sei (u,v) B-Kante bzgl. F → es existiert Weg v,…,u mit T-Kanten in F → Weg mit Kante (u,v) bildet Kreis in G. Petra Mutzel DAP2 SS08 67 67 Kreise und DFS • Methode: Erweitere DFS um Speicherung der B-Kanten: sobald wir von v aus auf einen markierten Knoten u treffen, haben wir eine B-Kante gefunden, falls u nicht der direkte Vorgänger (Elter) von v ist. Algorithmus ISACYCLIC(G) speichert dabei alle B-Kanten. Wenn man nur einen Kreis finden möchte, dann kann man abbrechen, sobald man eine B-Kante gefunden hat. Petra Mutzel DAP2 SS08 68 68 Algorithmus ISACYCLIC(G) Sei G=(V,E) ungerichteter Graph (1) function ISACYCLIC(Graph G=(V,E)) : bool { (2) B := ∅ (3) for all v∈V do { marked[v]:=0; π(v):=nil } (4) for all v∈V do { (5) if not marked[v] then (6) DFS-ACYCLIC(v) (7) } (8) if B ≠ ∅ then return false else return true (9) } Petra Mutzel DAP2 SS08 69 69 Algorithmus ISACYCLIC(G) ff (1) procedure DFS-ACYCLIC(Node v) { (2) marked[v] := true (3) for all w ∈ N(v) do (4) if not marked[w] then (5) π[w] := v (6) DFS-ACYCLIC(w) (7) else if π [v] ≠ w then (8) B := B ∪ (v,w) (9) } (10) } Petra Mutzel DAP2 SS08 70 70 Welche Kreise werden gefunden? Pfad von T-Kanten + eine B-Kante a c e f d g b Petra Mutzel DAP2 SS08 75 75 Test auf Kreisfreihet Theorem: Die Funktion ISACYCLIC(G) testet einen ungerichteten Graphen G=(V,E) auf Kreisfreiheit in Zeit Θ(|V|+|E|). Petra Mutzel DAP2 SS08 76 76 Petra Mutzel DAP2 SS08 77 77 Zum Ausschneiden und Üben Petra Mutzel DAP2 SS08 78 78 Q a d e b f c g