Graphen: Grundlegende Definitionen Graph G: abstrakte Struktur, die eine Menge von Objekten, oder Knoten V , mit zwischen diesen Objekten bestehenden Verbindungen, oder Kanten E, repräsentiert. Notation: Graph G(V , E) hat n Knoten und m Kanten. Graphen: Grundlegende Definitionen Mehrfachkanten sind möglich (z.B. U-Bahn Schienennetz) Gerichtete Graphen: Jede Kante hat eine Richtung Ungerichtete Graphen: Kanten verlaufen in beide Richtungen Graphen: Grundlegende Definitionen Weg: Folge von Knoten v1 , v2 , . . . , vk , die durch Kanten verbunden sind. Pfad: Weg, bei dem sich alle Knoten voneinander unterscheiden. Zyklus: Weg, bei dem sich nicht alle Knoten voneinander unterscheiden. Graph mit Zyklus heißt zyklisch. Graph ohne Zyklen heißt azyklisch. Repräsentation von Graphen Adjazenzliste: jeder Knoten v kennt eine Liste mit den Endpunkten der von v ausgehenden Kanten Platzbedarf O(n + m) Um Frage zu beantworten, ob Kante (u, v ) existiert, benötigt man O(deg(u)) = O(n) Zeit, deg(u) = Grad von u Adjazenzmatrix: binäre Matrix der Dimension n × n, die an Stelle (u, v ) genau dann eine 1 hat, wenn die Kante (u, v ) in G ist Platzbedarf O(n2 ) Um Frage zu beantworten, ob Kante (u, v ) existiert, benötigt man O(1) Zeit Gerichtete Azyklische Graphen Directed Acyclic Graphs (DAG) Kommen in vielen Anwendungen (z.B. Studienordnung) vor Quelle: Knoten mit Eingangsgrad Null Senke: Knoten mit Ausgangsgrad Null Satz: Jeder DAG hat mindestens eine Quelle und mindestens eine Senke. Topologische Sortierung Sortierung, in der für jede gerichtete Kante (u, v ) der Knoten u vor dem Knoten v in der Sortierung kommt, heißt topologische Sortierung Satz: Ein gerichteter Graph G besitzt genau dann eine topologische Sortierung, wenn G azyklisch ist. Algorithmus zum Finden einer topologischen Sortierung (Laufzeit O(n + m)): 1 2 3 4 Finde eine Quelle q von G Füge q als nächstes Element in der Sortierung ein Entferne q und alle von q ausgehenden Kanten von G Wiederhole bis G keine Knoten mehr enthält Tiefensuche Depth First Search (DFS) Möglichkeit zur Sortierung eines generellen Graphen Idee: G wird in einer Reihenfolge abgesucht, die immer zuerst in die Tiefe geht Jeder Knoten u von G bekommt u.d: Zeitstempel der ersten Prüfung des Knotens u.f : Zeitstempel, an der Algorithmus mit Knoten fertig ist u.predecessor : Vorgänger von u in der Suche Tiefensuche Depth First Search (DFS) Algorithm 1: Tiefensuche Algorithmus: 1 2 3 4 5 6 7 8 9 10 for jeden Knoten u von G do u.d = NULL u.predecessor = NULL end time = 0 for jeden Knoten u von G do if u.d == NULL then DFS-VISIT(G, u) end end Tiefensuche Depth First Search (DFS) Algorithm 2: DFS-VISIT(G, u): 1 2 3 4 5 6 7 8 9 10 time = time+1 u.d = time for jeden Knoten v mit (u, v ) ∈ G do if v .d == NULL then v .predecessor = u DFS-VISIT(G, v ) end end time = time+1 u.f = time Tiefensuche Depth First Search (DFS) Da jeder Knoten ein Mal bearbeitet wird und die Summe aller Kanten O(m) ist, ist die Laufzeit des Algorithmus O(m + n) Kanten (v .predecessor , v ) bilden einen Wald, der im Folgenden als Tiefenwald bezeichnet wird Tiefensuche hat viele Eigenschaften und Anwendungen. Hier wird nur eine Anwendung behandelt. Starke Zusammenhangskomponenten Definitionen: Graph G heißt Stark zusammenhängend von Knoten u aus falls es für einen beliebigen Knoten v einen gerichteten Weg von u nach v in G gibt. Stark zusammenhängend falls G von jedem Knoten aus stark zusammenhängend ist. Teilgraph G0 ist eine starke Zusammenhangskomponente falls G0 stark zusammenhängend ist und es keinen stark zusammenhängenden Teilgraphen von G gibt, der G0 echt enthält. Starke Zusammenhangskomponenten Algorithm 3: Starke Zusammenhangskomponenten finden (Algorithmus von Tarjan): 1 2 3 4 5 6 7 8 9 10 11 for jeden Knoten u von G do u.d = NULL u.lowlink = NULL end time = 0 S = leerer Stack for jeden Knoten u von G do if u.d == NULL then STRONG-CONNECT(G, u) end end Starke Zusammenhangskomponenten Algorithm 4: STRONG-CONNECT(G, u): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 time = time+1 u.d = time u.lowlink = time S.push(u) for jeden Knoten v mit (u, v ) ∈ G do if v .d == NULL then STRONG-CONNECT(G, v ) u.lowlink = min(u.lowlink , v .lowlink) end else if v ∈ S then u.lowlink = min(u.lowlink , v .d) end end if u.lowlink == u.d then Beginne eine neue starke Zusammenhangskomponente Z while v ! = u do v = S.pop() Füge v in Z ein end end Starke Zusammenhangskomponenten Grundidee des Algorithmus: Zyklen werden identifiziert Wurzel w ist der Knoten einer starken Zusammenhangskomponente, der vom Algorithmus zuerst durchlaufen wird Variable lowlink nimmt die Zeit w.d der Wurzel an Es ist immer u.lowlink ≤ u.d mit = für die Wurzel Wird ein Knoten als Wurzel w identifiziert, so ist jeder Knoten in S, der nach w eingefügt wurde in derselben starken Zusammenhangskomponente wie w Laufzeit (mit vorsichtiger Implementierung des Inhalts von S): analog zum Tiefensuchalgorithmus O(n + m)