VL-14: Graphalgorithmen I - Lehrstuhl für Informatik der RWTH Aachen

Werbung
Organisatorisches
VL-14: Graphalgorithmen I
• Vorlesung: Gerhard Woeginger (Zimmer 4024 im E1)
Sprechstunde: Mittwoch 11:15–12:00
• Übungen: Tim Hartmann, David Korzeniewski, Björn Tauer
Email: [email protected]
(Datenstrukturen und Algorithmen, SS 2017)
Gerhard Woeginger
• Webseite: http://algo.rwth-aachen.de/Lehre/SS17/DSA.php
• Nächste Vorlesung:
Dienstag, Juni 20, 16:15–17:45 Uhr, Aula 1
SS 2017, RWTH
DSAL/SS 2017
VL-14: Graphalgorithmen I
1/48
DSAL/SS 2017
VL-14: Graphalgorithmen I
2/48
Bedeutung der Graphen in der Informatik
Graphen treten in vielen Informatikanwendungen auf:
Beispiele
Graphalgorithmen I
I
(Computer-)Netzwerke
I
Darstellung von topologischen Informationen (Karten, . . . )
• Terminologie
• Repräsentation von Graphen
I
Darstellung von elektronischen Schaltungen
I
Vorranggraphen (precedence graph), Ablaufpläne, . . .
• Breitensuche (BFS)
• Tiefensuche (DFS)
• Finden von Zusammenhangskomponenten
I
Semantische Netze (z. B. Entity-Relationship-Diagramme)
DSAL/SS 2017
VL-14: Graphalgorithmen I
In dieser Vorlesung werden wir uns auf fundamentale Graphalgorithmen
konzentrieren.
3/48
DSAL/SS 2017
VL-14: Graphalgorithmen I
4/48
Was ist ein Graph? (1)
Was ist ein Graph? (2)
Gerichteter Graph
Beispiel
Ein gerichteter Graph (auch: digraph) G ist ein Paar (V , E ) mit
I
I
einer endlichen Menge V von Knoten (vertices) und
einer Menge E ⊆ V × V von geordneten Paaren von Knoten, die
gerichtete Kanten (arcs) genannt werden.
I
V = {A, . . . , F }
I
E = {(A, B), (A, D), (B, E ), (C , E ), (C , F ), (D, B), (E , D), (F , F )}
A
C
B
Kanten
Ungerichteter Graph
Knoten
Ein ungerichteter Graph G ist ein Paar (V , E ) mit
I
einer endlichen Menge V von Knoten (vertices) und
I
einer Menge E von ungeordneten Knotenpaaren, die Kanten (edges)
genannt werden.
DSAL/SS 2017
VL-14: Graphalgorithmen I
D
5/48
Terminologie (1)
F
E
DSAL/SS 2017
VL-14: Graphalgorithmen I
6/48
Terminologie (2)
I
Knoten u ist adjazent zu Knoten v , wenn (u, v ) ∈ E
I
Knoten u ist inzident zu Kante e = (u, v )
I
Kante (u, u) heisst Schlinge (loop)
I
Der Grad deg(u) ist die Anzahl der zu u inzidenten Kanten
I
In-Grad deg+ (u); Aus-Grad deg− (u)
I
Graph ist k-regulär, falls alle Knoten Grad k haben
Handshake Lemma
In einem ungerichteten Graphen G = (V , E ) gilt:
X
Teilgraph
Ein Teilgraph (subgraph) eines Graphen G = (V , E ) ist ein Graph
G 0 = (V 0 , E 0 ) mit:
I
V 0 ⊆ V und E 0 ⊆ E ∩ (V 0 × V 0 ).
I
Ist V 0 ⊂ V und/oder E 0 ⊂ E , so heisst G 0 echter (proper) Teilgraph
I
Ist E 0 = E ∩ (V 0 × V 0 ), so ist G 0 der durch V 0 induzierte Teilgraph
A
B
C
D
E
F
deg(v ) = 2|E |
v ∈V
Lemma
In gerichtetem Graphen gilt:
X
deg+ (v ) =
v ∈V
DSAL/SS 2017
VL-14: Graphalgorithmen I
X
deg− (v )
v ∈V
7/48
DSAL/SS 2017
VL-14: Graphalgorithmen I
Teilgraph in rot
8/48
Terminologie (3)
Terminologie (4)
Symmetrischer Graph
Vollständiger (und symmetrischer) Digraph
auf vier Knoten:
Der gerichtete Graph G heisst symmetrisch,
wenn aus (v , w ) ∈ E immer auch (w , v ) ∈ E folgt.
Zu jedem ungerichteten Graphen gibt es einen korrespondierenden
symmetrischen Digraphen.
A
B
D
E
Vollständiger Graph
Ein Graph G heisst vollständig, wenn jedes Paar von Knoten mit einer
Kante verbunden ist.
Kn ist der ungerichtete vollständige Graph mit n Knoten.
Transponieren
Der transponierte Graph von G = (V , E ) ist gegeben durch
G T = (V , E 0 ) mit (v , w ) ∈ E 0 gdw. (w , v ) ∈ E .
DSAL/SS 2017
VL-14: Graphalgorithmen I
9/48
DSAL/SS 2017
VL-14: Graphalgorithmen I
10/48
Pfade und Kreise (1)
Spaziergang, Pfad
Ein Spaziergang (walk) von einem Knoten v zu einem Knoten w ist eine
Folge v0 , v1 , v2 , . . . , vk−1 , vk , sodass:
Pfade, Kreise, Zusammenhang
I
Startknoten v0 = v und Endknoten vk = w
I
(vi , vi+1 ) ist Kante in E , für 0 ≤ i ≤ k − 1
Ein Spaziergang mit lauter paarweise verschiedenen Knoten (das heisst,
mit vi 6= vj für i 6= j) heisst simpel oder einfach oder Pfad.
Die Länge eines Spaziergangs ist die Anzahl der durchlaufenen Kanten.
Kreis
Ein Kreis (cycle) ist ein nicht-leerer Pfad bei dem Startknoten mit
Endknoten zusammenfällt.
Graph ist kreis-frei (azyklisch), wenn er keine Kreise als Teilgraphen hat.
DSAL/SS 2017
VL-14: Graphalgorithmen I
11/48
DSAL/SS 2017
VL-14: Graphalgorithmen I
12/48
Pfade und Kreise (2)
A
B
Zusammenhang (1)
C
Erreichbarkeit
Ein Knoten w ist vom Knoten v aus erreichbar,
wenn es einen Pfad von v nach w gibt.
Schlinge
D
E
Zusammenhang (im ungerichteten Graphen)
F
Kreis
I
G heisst zusammenhängend, wenn jeder Knoten von jedem anderen
Knoten aus erreichbar ist.
I
Eine Zusammenhangskomponente (connected component) von G ist
ein maximaler zusammenhängender Teilgraph von G.
A B E D B und C F F sind Beispiele für Spaziergänge.
E D B und C F sind Pfade.
DSAL/SS 2017
VL-14: Graphalgorithmen I
13/48
Zusammenhang (2)
DSAL/SS 2017
I
G heisst stark zusammenhängend (strongly connected), wenn jeder
Knoten von jedem anderen aus erreichbar ist.
I
G heisst schwach zusammenhängend, wenn der zugrunde liegende
ungerichtete Graph (in dem alle Kanten ungerichtet gemacht worden
sind) zusammenhängend ist.
C
B
A
I
F
J
Eine starke Zusammenhangskomponente von G ist ein maximaler
stark zusammenhängender Teilgraph von G.
G
H
D
I
Jeder ungerichtete Graph kann eindeutig in
Zusammenhangskomponenten aufgeteilt werden.
I
Jeder gerichtete Graph kann eindeutig in starke
Zusammenhangskomponenten aufgeteilt werden.
DSAL/SS 2017
14/48
Zusammenhang (3a)
Zusammenhang (im gerichteten Graphen)
I
VL-14: Graphalgorithmen I
VL-14: Graphalgorithmen I
E
Ein ungerichteter Graph: Wie lauten die Zusammenhangskomponenten?
15/48
DSAL/SS 2017
VL-14: Graphalgorithmen I
16/48
Zusammenhang (3b)
Zusammenhang (4)
C
B
A
I
F
J
G
H
D
E
Ein nicht-stark-zusammenhängender Digraph, aufgeteilt in seine
maximalen stark zusammenhängenden Teilgraphen.
Die Zusammenhangskomponenten.
DSAL/SS 2017
VL-14: Graphalgorithmen I
17/48
DSAL/SS 2017
VL-14: Graphalgorithmen I
18/48
Zusammenhang (5)
G ist Baum ⇐⇒ G zusammenhängend und G kreis-frei
Anmerkungen
I
Ein Baum mit n Knoten hat n − 1 Kanten.
I
Ein ungerichteter Graph mit n Knoten und n − 2 oder weniger
Kanten kann nicht zusammenhängend sein.
I
Ein ungerichteter Graph mit n Knoten und n oder mehr Kanten
muss einen Zyklus enthalten.
DSAL/SS 2017
VL-14: Graphalgorithmen I
Repräsentation von Graphen
19/48
DSAL/SS 2017
VL-14: Graphalgorithmen I
20/48
Repräsentation von Graphen: Adjazenzmatrix
Repräsentation von Graphen: Adjazenzliste
Sei G = (V , E ) ein Graph mit
• |V | = n
• |E | = m
• V = {v1 , . . . , vn }
Adjazenzliste
In der Darstellung als Array von Adjazenzlisten gibt es ein durch die
Nummer des Knoten indiziertes Array, das verkettete Listen
(Adjazenzlisten) enthält.
Der i-te Arrayeintrag enthält alle Kanten mit Startknoten vi .
Adjazenzmatrix
In der Adjazenzmatrix-Darstellung ist der Graph durch eine n × n Matrix
A gegeben, wobei
A(i, j) = 1 wenn (vi , vj ) ∈ E
A(i, j) = 0 wenn (vi , vj ) ∈
/E
I
Wenn G ungerichtet ist, ist A symmetrisch (A = AT ).
Dann muss nur die Hälfte der Matrix gespeichert werden.
I
Platzbedarf: Θ(n2 ).
DSAL/SS 2017
VL-14: Graphalgorithmen I
I
21/48
Darstellung eines ungerichteten Graphen
Ist G ungerichtet, dann wird jede Kante zweimal gespeichert
I
Kanten, die in G nicht vorkommen, benötigen keinen Speicherplatz
I
Platzbedarf: Θ(n + m).
DSAL/SS 2017
VL-14: Graphalgorithmen I
Darstellung eines gerichteten Graphen
A
A
B
C
E





DSAL/SS 2017
D
0
1
0
0
1
1
0
1
1
1
0
1
0
1
0
0
1
1
0
1
1
1
0
1
0
A
B
C
D
E
B
A
D
C
D
E
E
B
E
B
D
C
B
C
B
A
D
F
E
Adjazenzliste

Adjazenzmatrix
VL-14: Graphalgorithmen I
A
B
C
D
E
F
B
E
F
B
D
F
D
E
Adjazenzliste





22/48






23/48
DSAL/SS 2017
0
0
0
0
0
0
1
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
1
0
0
1
1
0
0
0
0
0
1
0
0
1







Adjazenzmatrix
VL-14: Graphalgorithmen I
24/48
Graphendurchlauf (1)
Viele Algorithmen (die wir später kennenlernen werden) untersuchen
jeden einzelnen Knoten und jede einzelne Kante eines Graphen.
Es gibt verschiedene Graphendurchlaufstrategien (traversal strategies),
die jeden Knoten (oder jede Kante) genau einmal besuchen:
Graphendurchlauf
DSAL/SS 2017
VL-14: Graphalgorithmen I
25/48
I
Breitensuche (BFS)
I
Tiefensuche (DFS)
I
Es handelt sich um Verallgemeinerungen der Strategien zur
Baumtraversierung
I
In diesem allgemeineren Szenario müssen wir uns aber alle bereits
besuchten Knoten explizit merken
I
Im folgenden arbeiten wir mit der Adjazenzlisten-Darstellung
I
Algorithmen auf dieser Basis kosten O(|V | + |E |) Zeit
DSAL/SS 2017
VL-14: Graphalgorithmen I
26/48
Graphendurchlauf (2)
Beispiele
I
Finden von (schwachen oder starken) Zusammenhangskomponenten
I
Topologische Sortierung
I
Kritische-Pfad-Analyse
I
Finden von 2-Zusammenhangskomponenten (biconnected
components)
I
und viele weitere . . .
DSAL/SS 2017
Breitensuche
VL-14: Graphalgorithmen I
27/48
DSAL/SS 2017
VL-14: Graphalgorithmen I
28/48
Breitensuche
Breitensuche: Beispiel
Breitensuche (Breadth-First Search, BFS)
A
Am Anfang sind alle Knoten als unbesucht (WHITE) markiert.
Die Breitensuche beginnt in einem beliebigen Knoten v .
B
Zugrundeliegende Strategie:
I
I
I
I
F
Markiere den aktuellen Knoten v als aktiv (GRAY).
Für jede Kante (v , w ) mit unbesuchtem Nachfolger w :
I
A
Suche gleichzeitig von allen derartigen Knoten w aus weiter.
Keinerlei Backtracking.
F
C
G
E
Erforsche alle folgenden unbesuchten Knoten
A
D
D
B
G
G
Markiere Knoten v als besucht (BLACK).
VL-14: Graphalgorithmen I
29/48
F
E
DSAL/SS 2017
C
E
Fertig!
VL-14: Graphalgorithmen I
30/48
Eigenschaften der Breitensuche (1)
void bfsSearch ( List adjList [ n ] , int n , int start ) {
int color [ n ];
Queue wait ; // zu verarbeitende Knoten
for ( int i = 0; i < n ; i ++) {
color [ i ] = WHITE ; // noch nicht gefunden
}
color [ start ] = GRAY ; // start ist noch zu verarbeiten
wait . enqueue ( start ) ;
while (! wait . isEmpty () ) {
// naechster noch u n ve ra rb e itet er Knoten
int v = wait . dequeue () ;
foreach ( w in adjList [ v ]) {
if ( color [ w ] == WHITE ) {
// neuer unbesuchter Knoten
color [ w ] = GRAY ; // w ist noch zu verarbeiten
wait . enqueue ( w ) ;
}
}
color [ v ] = BLACK ; // v ist abgeschlossen
}
}
VL-14: Graphalgorithmen I
C
Erforsche alle folgenden unbesuchten Knoten
Breitensuche: Implementierung
DSAL/SS 2017
E
D
B
G
B
Anmerkung: Die schwarzen Knoten sind genau jene Knoten, die vom
Startknoten aus erreichbar sind
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
C
A
Beginn der Breitensuche
F
DSAL/SS 2017
D
Notation
• Für einen Knoten v ∈ V bezeichnet d(v ) den Abstand vom
Startknoten zum Knoten v (das heisst: die Anzahl der Kanten auf dem
kürzesten Weg vom Startknoten nach v )
• Wenn ein Knoten w in die Queue gegeben wird, so färben wir die dafür
verantwortliche Kante (v , w ) gelb. Knoten v ist der Vater von Knoten w .
31/48
I
BFS besucht die Knoten in einer Reihenfolge mit ansteigendem
Abstand vom Startknoten
I
Erst wenn alle Knoten mit Abstand d verarbeitet worden sind,
werden die Knoten mit Abstand d + 1 angegangen
I
Die Suche terminiert, wenn für einen Abstand d keine Knoten
auftreten.
DSAL/SS 2017
VL-14: Graphalgorithmen I
32/48
Eigenschaften der Breitensuche (2)
I
Die zu verarbeitenden Knoten werden als FIFO-Queue (first-in
first-out) organisiert.
I
Es gibt eine einzige Verarbeitungsmöglichkeit für v (nämlich: wenn
es aus der Queue entnommen wird)
I
Die gelben Kanten induzieren den Breitensuchbaum
I
Breitensuchbaum hat Startknoten als Wurzel
Tiefensuche
Theorem (Komplexität der Breitensuche)
Die Zeitkomplexität von BFS ist O(|V | + |E |).
Der Platzbedarf von BFS ist Θ(|V |).
DSAL/SS 2017
VL-14: Graphalgorithmen I
33/48
Tiefensuche
DSAL/SS 2017
A
Am Anfang sind alle Knoten als unbesucht (WHITE) markiert.
Zugrundeliegende Strategie:
I
I
I
I
I
F
Suche rekursiv von w aus. Das heisst:
Erforsche Kante (v , w ), besuche den neu entdeckten Knoten w , und
forsche von dort aus, bis es nicht mehr weiter geht.
Dann backtracke von w nach v .
C
A
D
B
G
E
F
E
Erforsche einen Knoten
D
B
C
G
A
D
B
G
G
Überprüfe die Kante, ohne aber Nachfolger w zu besuchen.
Markiere Knoten v als besucht (BLACK).
F
C
E
Erforsche einen Knoten
Anmerkung: Auch bei DFS sind die schwarzen Knoten genau jene
Knoten, die vom Startknoten aus erreichbar sind
DSAL/SS 2017
A
Beginn der Tiefensuche
Für jede Kante (v , w ) mit bereits besuchtem Nachfolger w :
I
D
B
Markiere den aktuellen Knoten v als aktiv (GRAY).
Für jede Kante (v , w ) mit unbesuchtem Nachfolger w :
I
34/48
Tiefensuche: Beispiel (1)
Tiefensuche (Depth-First Search, DFS)
I
VL-14: Graphalgorithmen I
VL-14: Graphalgorithmen I
35/48
DSAL/SS 2017
F
C
E
Sackgasse!
Backtracke und erforsche den nächsten Knoten
VL-14: Graphalgorithmen I
36/48
Tiefensuche: Beispiel (2)
A
A
D
B
F
Tiefensuche: Beispiel (3)
C
E
F
A
D
B
F
C
A
E
F
B
E
F
C
A
G
DSAL/SS 2017
F
C
G
E
Erforsche den nächsten Knoten
A
D
D
B
G
G
E
B ist eine Sackgasse
Backtracke und erforsche den nächsten Knoten
VL-14: Graphalgorithmen I
F
C
E
F
Beide nächsten Knoten wurden bereits gefunden
37/48
DSAL/SS 2017
C
E
Fertig!
VL-14: Graphalgorithmen I
38/48
Eigenschaften der Tiefensuche (1)
void dfsRec ( List adjList [ n ] , int n ,
int start , int & color [ n ]) {
color [ start ] = GRAY ;
foreach ( next in adjList [ start ]) {
if ( color [ next ] == WHITE ) {
dfsSRec ( adjList , n , next , color ) ;
}
}
color [ start ] = BLACK ;
}
I
DFS erforscht einen Pfad so weit wie möglich (dann backtracking)
I
Die zu verarbeitenden Knoten werden in LIFO-Reihenfolge
abgearbeitet
Es gibt zwei mögliche Verarbeitungszeitpunkte für jeden Knoten:
I
1. Wenn der Knoten entdeckt wird
2. Wenn der Knoten abgeschlossen wird
void dfsSearch ( List adjList [ n ] , int n , int start ) {
int color [ n ];
for ( int i = 0; i < n ; i ++) { // I ni ti al i si er un g
color [ i ] = WHITE ;
}
dfsRec ( adjList , n , start , color ) ;
}
VL-14: Graphalgorithmen I
E
B
Tiefensuche – Implementierung
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
C
C wurde bereits gefunden
Backtracke und erforsche den nächsten Knoten
D
G
D ist eine Sackgasse
Backtracke und erforsche den nächsten Knoten
DSAL/SS 2017
C
D
B
G
G
Nächster Zustand wurde bereits gefunden
Backtracke und erforsche den nächsten Knoten
A
D
B
B
G
Nächster Zustand wurde bereits gefunden
Backtracke und erforsche den nächsten Knoten
A
D
Theorem (Komplexität der Breitensuche)
Die Zeitkomplexität von DFS ist O(|V | + |E |).
Der Platzbedarf von DFS ist Θ(|V |).
39/48
DSAL/SS 2017
VL-14: Graphalgorithmen I
40/48
Eigenschaften der Tiefensuche (2)
1
2
3
4
5
6
7
8
9
10
11
12
Eigenschaften der Tiefensuche (3)
void dfsRec ( List adjList [ n ] , int n ,
int start , int & color [ n ]) {
color [ start ] = GRAY ;
time = time+1; left[start] = time;
foreach ( next in adjList [ start ]) {
if ( color [ next ] == WHITE ) {
dfsSRec ( adjList , n , next , color ) ;
}
}
color [ start ] = BLACK ;
time = time+1; right[start] = time;
}
Fakt
Zwei Intervalle [left[v],right[v]] und [left[u],right[u]] sind
entweder disjunkt, oder das eine Intervall enthält das andere
Fakt
Knoten v ist Vorfahre vom Knoten u im DFS-Baum,
gdw. left[v] < left[u] < right[u] < right[v]
gdw. zum Zeitpunkt left[v] ein Pfad von v nach u existiert,
der (bis auf v ) nur weisse Knoten enthält
• Knoten v ist von Zeitpunkt left[v] bis Zeitpunkt right[v] aktiv
DSAL/SS 2017
VL-14: Graphalgorithmen I
41/48
DSAL/SS 2017
VL-14: Graphalgorithmen I
42/48
Eigenschaften der Tiefensuche (4)
DFS-Klassifikation von Kanten
I
Baum-Kanten (tree-edges) treten im DFS-Baum auf
I
Rückwärts-Kanten (back-edges) gehen von Knoten u zu Vorfahren v
I
Vorwärts-Kanten (forward-edges) gehen von einem Vorfahren v zum
Knoten u
I
Quer-Kanten (cross-edges) sind alle restlichen Kanten
Finden von
Zusammenhangskomponenten
Fakt
In einem ungerichteten Graphen ist jede Kante
entweder Baum-Kante oder Rückwärts-Kante
DSAL/SS 2017
VL-14: Graphalgorithmen I
43/48
DSAL/SS 2017
VL-14: Graphalgorithmen I
44/48
Finden von Zusammenhangskomponenten (1)
Finden von Zusammenhangskomponenten (2)
Problem
Bestimme alle Zusammenhangskomponenten für ungerichteten Graphen.
1
2
3
4
5
6
7
8
9
10
11
12
13
Lösung
I
I
Konstruiere den zugehörigen symmetrischen Digraphen
Verwende Tiefensuche:
I
I
I
Beginne bei einem beliebigen Knoten
Finde alle anderen Knoten (und alle Kanten) in der selben
Komponente mit DFS
Wenn es weitere Knoten gibt, wähle einen und wiederhole das
Verfahren
I
Man erhält einen Tiefensuchwald.
I
Die Zeitkomplexität ist Θ(|V | + |E |).
DSAL/SS 2017
VL-14: Graphalgorithmen I
45/48
Finden von Zusammenhangskomponenten (3)
1
2
3
4
5
6
7
8
9
10
11
DSAL/SS 2017
void connComp onents ( List adjLst [ n ] , int n , int & cc [ n ]) {
int color [ n ] , ccNum = 0;
for ( int v = 0; v < n ; v ++) { // I ni ti al i si er un g
color [ v ] = WHITE ;
}
for (int v = 0; v < n; v++) {
if (color[v] == WHITE) { // weitere Komponente
dfsSearch ( adjLst , n , v , ccNum++, cc) ;
}
}
}
DSAL/SS 2017
VL-14: Graphalgorithmen I
46/48
Organisatorisches
void dfsSearch ( List adjLst [ n ] , int n , int start ,
int & color [ n ] , int ccNum, int &cc[n]) {
color [ start ] = GRAY ;
cc[start] = ccNum; // speichere Nummer der Komponente von v
foreach ( next in adjLst [ start ]) {
if ( color [ next ] == WHITE ) {
dfsSearch ( adjLst , n , next , color , ccNum, cc) ;
}
}
color [ start ] = BLACK ;
}
VL-14: Graphalgorithmen I
// Ausgabe in cc : cc [ v ] = Komponente von Knoten v
• Nächste Vorlesung:
Dienstag, Juni 20, 16:15–17:45 Uhr, Aula 1
• Webseite: http://algo.rwth-aachen.de/Lehre/SS17/DSA.php
47/48
DSAL/SS 2017
VL-14: Graphalgorithmen I
48/48
Herunterladen