 
 
 
9   Graphen
Literatur:
 Kapitel 23 aus [CLR90]. Breitensuche
 stammt von Moore [Moo59]. Der Erfinder der Tiefensuche
 scheint unbekannt. Der angegebene Algorithmus für die starken
 Zusammenhangskomponenten ist von Tarjan [Tar72].
Inhalt
 Graphen ·Repräsentierungen ·Erreichbarkeit ·Breitensuche ·Tiefensuche ·backtracking ·topologisches
 Sortieren ·Berechnung starker Zusammenhangskomponenten
- 
 Wichtige Klasse von Algorithmen, viele Anwendungen
-   Problem:  Durchlaufen eines gegebenen Graphen,
 mit Besuch der Knoten + Berechnung von Strukturinformation über den
 Graphen
-  Unterscheidung nach 
 
- 
   Durchlaufordnung und (davon abhängig)
 
-  Art der  Zusatzinformation über den Graphen und
 seine Struktur: z. B.
 
- 
  gegenseitige  Erreichbarkeit von Knoten
 
-   Zyklen
 
-   Bäume als Teilstrukturen im Graphen
 
-  (stark)  zusammenhängende Teilgraphen
 
 
 
-  Zwei  Klassiker:
 
- 
  Tiefensuche
 
-  Breitensuche
 
 
  | Graphen und ihre Repräsentierung | 
 Definition 4  [Graph]  
 
 Ein (gerichteter) Graph G = (V,E): V Menge von
  Knoten, EÍ V × V  Kanten.
  
 
- 
  Zwei  Standardmöglichkeiten: 
 
- 
   Adjazenzlisten
 
-   Adjazenzmatrix
 
 
  | Adjazenzlistendarstellung | 
 
- 
  Graph G = (V,E) dargestellt durch  Array
 Adj von |V|  Listen von Knoten
 
-  für alle v Î V: Adj[v] = Liste der zu v
 adjazierenden Knoten
 
-  Speicherbedarf: O(V+E)
 
-  robuste Darstellung, anwendbar auf viele Arten von Graphen
 
-  Nachteil: List suche zum Finden der Nachbarn.
 
  | Adjazenzmatrixdarstellung | 
 
- 
  Gegeben Graph G = (V,E), die Knoten V seien beliebig numeriert
 von 1,... |V|.
 
-  Darstellung als Matrix A = (a)ij mit
 
|  | 
| aij | = | 
| ì í
 î
 |  | 
| 1 |  | falls (ui,uj) Î E |  | 0 |  | sonst |  |  |  |  |  
 
-  Speicherbedarf O(V2)
 
-  Nachteil: für dünnbesetzte Matrizen verschwenderisch
 
-  Vorteile
 
- 
  einfache Darstellung, für kleine Graphen gut
 
-  Transponieren der Matrix = Umkehren der Pfeilrichtung
 
 
- 
 klassischer Graphsuchalgorithmus, Reihe von
  Verallgemeinerungen
-  ursprüngliches Problem [Moo59]: kürzeste Wege in
 Labyrinthen
-  Problem:
 
- 
  gegeben: G = (V,E) + einen ausgezeichneten Knoten
 (Quelle) sÎ V.
 
-  gesucht: Alle von s aus erreichbaren Knoten
 
-  Zusatzinfo:
 
- 
  Abstand d(s,u) jeden Knotens u zu
 s9
 
-  Breitensuchbaum: Baum mit kürzesten Pfaden von
 s zu jedem Knoten.
 
 
 
- 
   Grundgedanke der Breitensuche:
 
 
 | 
 Suche ``in die Breite'': Bevor Knoten mit Abstand k+1 von der
 Quelle entdeckt (bzw. abgehandelt) werden, sind alle Knoten
 mit Abstand k" entdeckt (bzw. abgehandelt)
  |  
 
 
-  Sicherstellung der Breitendurchlaufordnung:
 
 | 
 Behandlung der entdeckten Knoten in Fifo-Manier (Queue)
  |  
 
 
- 
 Zur Erkennen von bereits gesehenen Knoten:  3
 Farben:10
 
- 
  weiß: unentdeckt
 
-  grau: entdeckt, aber noch nicht fertigbehandelt
 
-  schwarz: fertig (und damit auch entdeckt)
 
 
-  Reihenfolge:  weiß Þ grau Þ
 schwarz11
-  Zusatzinfo:
 
- 
   Distanz: Distanz des Knotens, von dem man entdeckt wird + 1
 
-   Breitensuchbaum: Merken des Knotens, von dem man
 entdeckt wird.
 
 
 
 
bfs (G,s) =      // Graph und Quellknoten 
  for each vertex u in V[G] - {s}
  do color[u] := white
     d[u]     := $\infty$;
     p[u]     := nil
  done;
  color[s]    := gray;
  d[s]        := 0;
  p[s]        := nil;
  Q.enqueue(s);                 // beginne mit der Quelle
  while not Q.isempty()
  do
     u := Q.head();             // noch kein dequeue!
     for each v in Adj[u]       // alle Nachbarn
     do
        if   color[v] = white
        then color[v] := gray;
             d[v]     := d[u] + 1;
             p[v]     := u;
             Q.enqueue(v)      // speichere den Nachbarn
        fi;
     done;
     Q.dequeue()            // u ist abgearbeitet
     color(u) := black;     // und als fertig markiert
  done
 
  | Breitensuchalgorithmus: Analyse | 
- 
  Zeitkomplexität:
 
- 
  jeder Knoten wird höchstens einmal in Q eingereiht, und damit
 auch höchstens einmal aus ausgereiht:
  O(V)12
 
-  für jeden Knoten wird die Adjazenzliste durchlaufen, insgesamt
 höchstens  O(E)
 
 
- Þ O(E+V) 
 Siehe Vorlesung
 
- 
 Suche zunächst in die  Tiefe13
-  Wie bei Breitensuche:  Farben zur Suchsteuerung
 
- 
  Weiß: ungesehen
 
-  Grau: entdeckt
 
-  Schwarz: fertig 
 
 
-  anstelle von  Queue: Stack
-  ``Backtracking''
-  Teilgraph der Vorgänger Gp= (V,
 Ep): 
 
| E |  | ={(p(v),v) | vÎ V, p(v) ¹ ^} |  
 
- Þ Menge von  Tiefensuchbäumen (=
  Tiefensuchwald, depth-first forest)
-  Zusatzinformation:  Zeitstempel   d[v] (discovered) und
  f[v] (finished)  wobei d[v] < f[v].
 
DFS(G)                           // Gerichteter Graph
  for all vertices $v \in V[G]$  // Initialisierung
  do 
     color[u] := white;          
      pred[u] := nil; 
  od
  time := 0;                     // Ende der Initialisierung
  for all vertices  $u \in V[G]$
  do
      if color[u] = white then DFS-Visit(u); // eigentl. Tiefensuche
  done;
 
 
 
 
 
DFS-visit(u)                       // Eigentliche Tiefensuche
  color[u] := gray;                // Knoten entdeckt
  time := time + 1;  d[u] := time; // merke Entdeckungszeit
  for each v in Adj[u]             // erkunde Kante (u,v)
  do  if   color[v] = white
      then pred[v] := u            // pred = Entdeckerknoten
           DFS-visit(v)            // rek. Abstieg
  done;
  color[u] := black;               // u ist fertig, merke
  time := time + 1; f[u] := time;  // die Fertigstellzeit
 
- 
 Bild 23.5(1) 
-   Komplexität: 
 
- 
  O(V) für DSF 
 
-  O(E) für DFS-Visit 
 
- Þ Laufzeit für Tiefensuche: O(E+V)
 
 
 
[Klammerung]
 Gegeben zwei Knoten u, v aus G = (V, E). Es gilt genau eine der
 folgenden Bedingungen:
- 
  [d[u],f[u]] ist echtes  Teilintervall von [d[v],
 f[v]], u ist  Nachfahre von v in Tiefensuchbaum
 
-  Symmetrisch.
 
-  [d[u],f[u]] und [d[v], f[v]] disjunkt 
 
- 
Þ  Verschachtelung der Intervalle:
Knoten v ist echter  Nachfolger von u im Tiefensuchwald
 gdw.  d[u] < d[v] < f[v] < f[u]
- 
 Klassifikation von Kanten:
 
- 
  Baumkante: Kanten des  Tiefensuchwaldes
 
-  Rückwärtskante: Kanten von Nachfahren zu Vorfahren
 in einem Tiefensuchbaum (inkl. Selbst-Schleifen)
 
-  Vorwärtskante: Kante von Vorfahren zu Nachfahren
 gemäß einen Tiefensuchbaumes, die  nicht im Baum sind
 
-  Querkante: alle anderen
 
 
- 
 Anwendung der  Tiefensuche
-  algorithmische Umsetzung der bekannten Tatsache:  jede
 Halbordnung läßt sich zu einer totalen/linearen Ordnung erweitern.
-  Graphdarstellung einer Halbordnung:  DAG =
  "`Gerüst"' der Ordnung
Zunächst ein paar Definitionen:Definition 5  
 Gegeben eine binäre Relation R. Die reflexive, transitive
 Hülle R of R ist gegeben als die kleinste binäre Relation
 sodaß x R x und x R y R z impliziert x R
 z. Mit R+ ist die Relation R R gemeint.
Definition 6  
 Eine binäre Relation R Í S × S auf einer Menge S ist
 eine Halbordnung (Notation dann oft £ für R)
 wenn sie  reflexiv,  transitiv, und
  antisymmentrisch ist. Eine totale oder
 lineare Ordnung ist eine Halbordnung mit der
 zusätzlichen Eigenschaft der Vergleichbarkeit.
 (" x,y. x£ y oder y£ x)
Definition 7  [DAG]  
 Ein gerichteter, azyklischer Graph (DAG) ist ein
 gerichteter Graph ohne Zyklen, d.h., es für alle Knoten u, v Î V
 gilt: Wenn u ®+ v, dann u ¹ v.
- 
 Eine topologische Sortierung eines DAGs G ist eine
 lineare Ordnung der Knoten von G, Kompatibilität: falls (u,v) eine
 Kante auf G, dann u < v in der linearen Ordnung.
-  d.h., kein Sortieren wie bei den  Sortieralgorithmen
-  Bild
-  Lineare Ordnung = Zeitstempel der Schwarzfärbung =
 f-Zeit. 
  | Topologisches Sortieren (2) | 
 
- 
  Eingabe: DAG ("`="' Halbordnung), Ausgabe: verzeigerte Liste ("`="'
 lineare Ordnung)
 
 
Topological-Sort(G)               // G ist ein DAG
  call DFS(G) to compute  finishing-times f[v] for all v;
  as each vertex is finished, insert it onto the front of a list;
  return the linked list
 
 
- 
   Komplexität: Laufzeit O(V+E).
 
 
  | Topologisches Sortieren (3) | 
Satz 2  [Weiße Pfade]  
 Gegeben Graph G. Im Tiefensuchwald für G gilt: v ist
  Nachfahre von u gdw. zur Zeit d[u] der Knoten v durch
 u auf einem  weißen Pfad erreichbar ist.14
 
Lemma 3  
 Ein gerichteter Graph ist  azyklisch gdw. die Tiefensuche
  keine Rückwärtskanten produziert.
Beweis:
 
 Sei G zyklisch Þ es gibt einen Zyklus c: w ®+ w.
 Sei v der  erste entdeckte Knoten aus c und u sein
  Vorgänger im Zyklus. Zur Zeit d[v]:  weißer
 Pfad v ®* u  Þ  u wird Nachfolger im
 Tiefensuchwald Þ Behauptung ((u,v) ist Rückwärtskante).
 
 Þ 
 DFS findet Rückwärtskante (v,u), andererseits u ®* v
 mittels Baumkanten (weil u Vorgänger von v im Tiefensuchbaum)
Lemma 4  [Korrektheit]  
 Der Algorithmus angewandt auf einen DAG, liefert eine
  topologische Sortierung.
Beweis:
 Prüfen der Kompatibilität: falls u ®+ v in G, dann f[u] <
 f[v]. Sei (u,v) eine Kante. Wenn sie mit DFS erkundet wird,
 ist v  nicht Grau (sonst wäre v ein Vorfahre Þ
 Rückwärtskante) Þ v ist  weiß oder
  schwarz.
 v weiß
 v wird Nachfahre von u Þ f[v] < f[u] 
 
 v schwarz
 Dann sofort f[v] < f[u]
  | Starke Zusammenhangskomponenten | 
- 
 klassische Anwendung der DFS: Zerlegen eines Graphen in
  stark-zusammenhängende Komponenten
Definition 8  [Starker Zusammenhang]  
 
 Gegeben gerichteter Graph G = (V, E). Eine starke
 Zusammenhangskomponente von G ist eine maximale
 Knotenmenge UÍ V mit: für alle u1, u2 Î U gilt
 u1®* u2 und u2®*u1.
 
- 
  Idee: G und Gt haben die selben starken
 Zusammenhangskomponenten Þ  zweimaliges Anwenden von
 DFS, auf G und auf den  transponierten
 Graphen15 Gt
-   Komplexität: lineare Zeitkomplexität
 O(E+V) 
 
 
 
Strongly-connected-components(G)    // G: gerichteter Graph
  call DFS(G) to obtain finishing times f[u] for the vertices;
  compute $G^t$ = transpose(G)        // transponiere G
  call DFS($G^t$), but in the main loop of DFS, 
       consider the vertices in order of 
       decreasing f[u]
  output the vertices of each tree in the depth-first 
      forest from the previous step as a separated strongly 
      connected component.
 
 
 
- 
 Vorvater f(u) eines Knoten u ist derjenige Knoten
 w mit u®* w und maximalen f[w]. 
Lemma 5  
 Der Vorvater f(u) bzgl. einer Tiefensuche ist ein
  Vorgänger von u,
 Für alle Knoten u gilt: u und f(u) liegen  in der
 selben starken Zusammenhangskomponente.
Beweis:
 Folgt direkt aus dem vorangegangenen und der Definition von Vorvater.
- 
 es gilt also: für jede SCC ist der  Vorvater 
 
- 
  der erste Knoten der entdeckt wird und
 
-  der letzte Knoten der beendet wird!
 
 
- Þ
 
 | die starke Zusammenhangskomponente von r sind
 diejenigen Knoten die von r in Gt erreichbar sind. |  
 
 
February 5, 2002
 
 
