Algorithmen und Datenstrukturen 15. Vorlesung

Werbung
Minimale Spannbäume – 1
Erinnerung: Ein (freier) Baum ist ein zusammenhängender
(zh)und kreisfreier (ungerichteter) Graph G = (V, E).
Algorithmen und Datenstrukturen
15. Vorlesung
Nach Charakterisierungssatz gilt für Baum G = (V, E):
(1) G hat |V |−1 Kanten.
Karl-Heinz Niggl
(2) Durch Hinzufügen einer neuen Kante (u, w) entsteht
genau ein Kreis:
11. Juli 2006
u
Bsp.
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
FG KTuEA, TU Ilmenau
Minimale Spannbäume – 2
w
AuD – 11.07.2006
1
Minimale Spannbäume – 3
(3) Durch Streichen einer Kante e := (u, w) zerfällt G in
zwei Komponenten:
Def. Sei G = (V, E) ein zusammenhängender Graph. Ein
Spannbaum von G ist ein Baum (V, T ) mit T ⊆ E.
U := {v ∈ V | v ist von u aus in (V, E −e) erreichbar}
Bsp. (V, T ) ist ein Baum von G
W := {v ∈ V | v ist von w aus in (V, E −e) erreichbar}
W
U
Bem. Jeder zusammenhängende Graph hat einen Spannbaum.
Bsp.
FG KTuEA, TU Ilmenau
u
AuD – 11.07.2006
w
Iterativ: Solange ein Kreis (u, v1, . . . , vk−1, v, u) existiert, entferne eine Kreiskante, z.B. e = (v, u). Der ZH bleibt erhalten:
In Wegen mit e ersetze e durch (v, vk−1, . . . , v1, u). 2
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
3
Minimale Spannbäume – 4
Minimale Spannbäume – 5
Def. Ein gewichteter Graph ist ein Tripel G = (V, E, c) mit
Graph (V, E) und Gewichtsfunktion (Kostenfkt.) c : E → R.
Def. Jeder Kantenmenge E ′ ⊆ E in einem gewichteten Graphen G = (V, E, c) wird durch
P
c(e)
c(E ′) :=
3
5
1
2
2
1
4
e∈E ′
1
5
ein Gesamtgewicht zugeordnet.
4
3
Bsp.
3
3
1
2
1
4
2
1
5
3
1
2
2
3
1
5
4
3
1
2
Bsp. Gesamtgewicht 30
2
3
1
1
2
3
3
Gewichte modellieren z.B. die Distanz zwischen zwei Städten.
1
2
Ziel: Finde Spannbaum mit minimalen Kosten.
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
4
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
5
Minimale Spannbäume – 6
Minimale Spannbäume – 7
Def. Ein Spannbaum (V, T ) eines zusammenhängenden Graphen G = (V, E, c) heißt minimaler Spannbaum, kurz MST
(minimum spanning tree), falls gilt:
Ziel: Algorithmus von Jarnı́k/Prim
Berechnet bei Eingabe eines zusammenhängenden Graphen
G = (V, E, c) einen minimalen Spannbaum von G in Zeit
O((|V |+|E|)·log |V |).
c(T ) = min{c(T ′) | (V, T ′) Spannbaum von G}
Bsp. Zwei minimale Spannbäume mit Gesamtgewicht 18.
3
5
1
1
2
2
3
4
1
5
5
1
1
1
2
2
3
3
1
1
FG KTuEA, TU Ilmenau
2
Hier: Wähle eine Kante nach der anderen, wobei jeweils eine
billigste Kante ausgewählt wird, die mit den bereits gewählten
(T ) zusammenhängt und einen neuen Knoten erreicht (S).
3
3
1
5
3
3
1
1
4
3
2
2
2
4
Verwendetes Algorithmenparadigma: greedy ( gierig“)
”
Allgemein: Baue eine Lösung Schritt für Schritt auf und
treffe in jedem Schritt die (lokal) günstigste Entscheidung.
4
2
AuD – 11.07.2006
3
1
2
6
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
7
Minimale Spannbäume – 8
Minimale Spannbäume – 9
Algorithmus von Jarnı́k/Prim ohne Implementierungsdetails
procedure jarnı́k/prim-mst-abstract(G = (V, E, c))
Wähle einen beliebigen Knoten u0 ∈ V .
S ← {u0}
⊲ bisher erreichte Knoten
⊲ bisher gewählte Kanten
T ←∅
Wiederhole (n−1)-mal (n := |V |)
Finde w ∈ S und u ∈ V \S mit: c(w, u) ist minimal
unter allen Werten c(w′, u′), w′ ∈ S, u′ ∈ V \S.
S ← S ∪ {u}
T ← T ∪ {(w, u)}
return T
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
8
Bsp.
1
3
5
1
2
2
3
4
1
5
5
1
1
1
1
2
→
2
3
5
3
1
1
→
2
3
3
3
1
1
2
FG KTuEA, TU Ilmenau
3
4
1
5
5
2
AuD – 11.07.2006
9
1
1
1
2
→
2
AuD – 11.07.2006
1
5
5
1
1
1
→
2
3
1
10
5
1
2
→
3
3
1
1
3
2
3
2
2
2
4
4
3
1
3
1
2
2
2
3
4
4
3
FG KTuEA, TU Ilmenau
2
3
3
1
3
1
→
2
3
3
3
1
3
3
1
2
2
4
3
2
4
3
1
2
2
4
2
1
Bsp. fortgesetzt
1
1
5
5
4
Minimale Spannbäume – 11
Bsp. fortgesetzt
5
1
1
4
Minimale Spannbäume – 10
3
2
2
3
4
FG KTuEA, TU Ilmenau
3
1
2
AuD – 11.07.2006
2
11
Minimale Spannbäume – 12
Minimale Spannbäume – 13
Bsp. fortgesetzt
3
5
Bsp. fortgesetzt
1
1
2
2
3
4
1
5
5
1
1
→
2
2
3
1
1
2
→
2
FG KTuEA, TU Ilmenau
1
12
2
2
5
3
2
3
1
1
AuD – 11.07.2006
2
→
2
FG KTuEA, TU Ilmenau
3
1
2
AuD – 11.07.2006
13
Wegen |Tn−1| = n−1 und Charakterisierungssatz (1) gilt dann
T = Tn−1 für den nach Beh. exisitierenden MST T von G.
3
FG KTuEA, TU Ilmenau
1
Beh. Für i < n gilt: ∃ MST T von G mit T ⊇ Ti.
2
3
1
3
1
Nach Konstruktion gilt für i < n: (Si, Ti) ist ein Baum.
Wir beweisen die folgende Schleifeninvariante:
4
→
5
Korrektheit: (jarnı́k/prim-mst-abstract(G)): Sei
• Ti die Kantenmenge (Größe i) nach Runde i,
• Si die Knotenmenge (Größe i + 1) nach Runde i.
4
1
1
Minimale Spannbäume – 15
Bsp. fortgesetzt
1
2
2
3
Minimale Spannbäume – 14
5
→
2
3
1
2
1
1
3
2
3
AuD – 11.07.2006
3
5
5
4
4
3
1
3
2
1
1
4
3
1
2
2
3
4
3
3
1
3
1
5
5
3
3
1
1
1
4
3
2
2
2
1
4
3
4
2
Beweis. Induktion nach i < n. i = 0 Dann gilt T0 = ∅, fertig
nach Bem.
14
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
15
Minimale Spannbäume – 16
Minimale Spannbäume – 17
i → i+1 Nach I.V. gibt es einen MST T von G mit T ⊇ Ti.
Werde e = (w, u) in Runde i+1 < n gewählt, d.h. Ti+1 = Ti ∪{e}
mit w ∈ Si, u ∈ V \Si und c(e) ist diesbzgl. minimal.
e ∈ T Dann gilt T ⊇ Ti+1, fertig. e ∈
/ T Nach Satz existiert in
T ein eindeutig bestimmter einfacher Weg(w, v1, . . . , vr−1, u).
Dieser Weg beginnt in Si und
endet außerhalb von Si.
w
e
e’
u
FG KTuEA, TU Ilmenau
u
Setze T ′ := (T ∪ {e})−e′.
T ′ besteht aus n−1 Kanten, c(T ′) ≤ c(T ) und T ′ ist kreisfrei.
(T ∪{e} hat genau einen Kreis, nämlich (w, v1, . . . , vr−1, u, w).
Durch das Entfernen von e′ wird dieser Kreis zerstört.)
Auf dem Weg existiert also eine
Kante e′ = (w′, u′) mit w′ ∈ Si
und u′ ∈
/ Si .
AuD – 11.07.2006
Aufgrund der Minimalität von
c(e) muß c(e′) ≥ c(e) gelten,
sonst wäre e in Runde i nicht
gewählt worden.
w
Also ist T ′ ein Spannbaum von G mit T ′ ⊇ Ti+1. Da T ein
MST ist, folgt c(T ′) = c(T ), d.h. T ′ ist auch ein MST.
16
FG KTuEA, TU Ilmenau
Minimale Spannbäume – 18
AuD – 11.07.2006
17
Minimale Spannbäume – 19
Implementierung
Annahme: G = (V, E, c) mit V = {1, . . . , n} ist in Adjazenzlistendarstellung gegeben. Die Kantengewichte c(e) stehen in
den Adjazenzlisten bei den Kanten.
In Runde i erhält man mittels heap-extract-min den neuen
Knoten ui für S sowie die neue Kante (p(ui), ui) für T .
Berechnung der d-Werte: Für jede Kante (ui, u) mit u ∈
/ Si
prüfe, ob c(ui, u) < aktueller Wert d(u).
Für Knoten u ∈ V \S mit Nachbarn in S wollen wir wissen:
1) Distanz d(u) von u zu S := min{c(w, u) | w ∈ S},
2) den (einen) Knoten p(u) := w ∈ S mit c(w, u) = d(u).
Falls u ohne Nachbarn in S, gelte d(u) = ∞ und p(u) = NIL.
Trick: Verwalte die Knoten u ∈ V \S mit Werten d(u) < ∞
als Prioritäten in einer Min-Priority-Queue PQ.
Wenn d(u) = ∞ gilt, ist u (noch) nicht in PQ.
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
4
5
→
3 6
4
ui
2
5
3 6
ui
2
7
7
Falls ja:
d(u) ← c(ui, u)
p(u) ← ui
Die Priorität von u in
PQ erniedrigt sich:
heap-decrease-key!
x ∈ PQ: x = (key[x], node[x], p[x]), key[x] = c(p[x], node[x]).
18
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
19
Minimale Spannbäume – 21
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
Minimale Spannbäume – 22
procedure jarnik/prim-mst(G = (V, E, c))
S ← {1}
⊲ Startknoten u0 = 1
T ←∅
⊲ bisher gewählte Kanten
empty(PQ)
⊲ leere Priority-Queue
foreach Nachbar u von u0 do
⊲ nach Adjazenzliste von u0
min-heap-insert(PQ, (c(u0, u), u, u0))
Wiederhole (n − 1)-mal (n := |V |)
(k, v, w) ← heap-extract-min(PQ)
S ← S ∪ {v}
⊲ der neue Knoten
T ← T ∪ {(w, v)}
⊲ die neue Kante
⊲ nach Adjazenzliste von v
foreach Nachbar u von v do
if u ∈
/ S and u ∈
/ PQ then
min-heap-insert(PQ, (c(v, u), u, v))
if u ∈
/ S und (cu, u, wu) in PQ mit c(v, u) < cu then
heap-decrease-key(PQ, u, (c(v, u), v))
return T
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
20
Tiefensuche in Digraphen – 1
Laufzeitanalyse: Realisiere Menge S über einen Bitvektor
der Länge n = |V |, Zugriffszeit O(1), Menge T z.B. als Liste.
Die Min-Priority-Queue PQ enthält maximal n−1 Einträge.
Initialisierung benötigt Zeit O(deg(u0) · log n).
Die while-Schleife wird (n−1)-mal durchlaufen, Kosten O(n)
für die Schleifenorganisation. Im Schleifenrumpf benötigt jeder der Aufrufe heap-extract-min, min-heap-insert oder
heap-decrease-key Zeit O(log n). Insgesamt (m := |E|):
P
n · O(log n) +
O(deg(ui) · log n)
0≤i<n
P
deg(ui) = O((n+m)·log n) = O n log n+log n·
0≤i<n
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
21
Tiefensuche in Digraphen – 2
1. Tiefensuche dfs(v) (depth-first-search) von v aus:
Man muß verhindern: Mehrfache Entdeckung eines Knoten v
Eingabeformat: Digraph G = (V, E) in Adjazenzlistenkodierung mit Knotenmenge V = {1, . . . , n}
Dazu verwende Statusinformation neu, aktiv, fertig.
Ziele:
• Besuche alle von v aus erreichbaren Knoten und Kanten.
• Sammle Strukturinformationen
(Farbe weiß)
v neu: – noch nie gesehen
(Farbe rot)
v aktiv: – dfs(v) gestartet, noch nicht beendet
v fertig: – dfs(v) beendet
(Farbe grau)
Der Status wird in einem Array status[1..n] verwaltet.
Parallel dazu (oder Teil davon) existiert Array nodes[1..n].
Ansatz: Besuche Knoten v und bearbeite nacheinander alle
Nachbarn v1, . . . , vl von v. Aber sobald ein neuer Knoten vi
entdeckt wird, rufe sofort rekursiv dfs(vi) auf.
(Weitere Nachbarn von v kommen später dran.)
Effekt: Entdeckung der Knoten geht vorrangig in die Tiefe.
Initialisierung: Der Status aller Knoten ist anfangs neu.
Knotennumerierung erfolgt in der Reihenfolge der Entdeckung:
Array dfs num[1..n]: Tiefensuch-Numerierung.
Mitzählen erfolgt in dfs count, mit 0 initialisiert.
FG KTuEA, TU Ilmenau
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
22
AuD – 11.07.2006
23
Tiefensuche in Digraphen – 3
Tiefensuche in Digraphen – 4
4
Input: Knoten v mit status(v) = neu.
⊲ Tiefensuche von v aus, rekursiv
1: procedure dfs(v)
2:
dfs count ← dfs count+1
3:
dfs num(v) ← dfs count
4:
status(v) ← aktiv
5:
foreach w ∈ suc(v) do
⊲ nach Adjazenzliste von v
⊲ w wird entdeckt
6:
if status(w) = neu then
dfs(w)
7:
8:
status(v) ← fertig
AuD – 11.07.2006
24
1
1
2
2
1
2
6
2
6
1
2
8
4
1
1
2
2
1
7→
2
6
3
1
2
8
4
1
1
2
2
2
6
1
1
3
1
1
AuD – 11.07.2006
1
2
8
2
6
3
1
2
8
4
1
1
2
2
7→
2
6
FG KTuEA, TU Ilmenau
1
3
1
7
2
8
5
1
1
2
2
2
6
5
3
9
1
1
10
9
1
4
2
5
3
2
1
10
9
1
1
1
2
1
2
1
8
2
6
3
5
1
1
2
2
3
1
2
2
6
10
26
FG KTuEA, TU Ilmenau
1
1
1
8
3
AuD – 11.07.2006
1
2
8
5
3
10
9
1
4
1
2
5
3
1
10
9
1
4
2
1
7
2
2
2
3
7→
1
2
1
7
1
1
4
2
2
3
7→
1
2
4
1
2
2
2
7
1
2
10
4
3
25
1
3
7→
3
9
5
AuD – 11.07.2006
4
1
1
2
Tiefensuche in Digraphen – 6
3
2
3
10
4
2
1
1
1
2
3
5
2
9
1
2
1
1
4
2
8
2
7
1
2
1
1
3
7→
10
4
2
1
7
2
2
2
3
7→
FG KTuEA, TU Ilmenau
1
2
9
2
1
7
1
1
1
2
4
1
1
2
2
2
3
1
7
1
1
2
4
1
4
2
1
7
1
1
2
3
7→
3
3
2
3
Bsp.
Tiefensuche in Digraphen – 5
4
1
2
2
Tiefensuche von v0 aus: Initialisiere dfs count mit 0 und
den Status aller Knoten mit neu. Rufe dfs(v0) auf.
FG KTuEA, TU Ilmenau
1
1
2
5
3
2
1
9
10
27
Tiefensuche in Digraphen – 7
4
1
1
2
2
1
7→
2
6
3
1
7
2
8
4
1
1
2
2
1
2
6
1
7
1
2
8
4
1
1
2
2
2
6
FG KTuEA, TU Ilmenau
1
1
1
3
1
2
8
3
10
9
1
4
1
2
1
w,
10
1
4
2
Mindestens einmal Angenommen, es gäbe in G einen Weg
v0 = v, v1, . . . , vr−1, w, aber dfs(w) wird nicht aufgerufen. O.E.
habe dieser minimale Länge mit dieser Eigenschaft.
5
3
2
1
G
Beweis. Höchstens einmal Bei Aufruf dfs(w) gilt status(w)
= neu; dann wird der Status sofort auf aktiv gesetzt und nie
wieder auf neu.
5
3
9
2
1
7
Lemma (Beobachtung 1 zu dfs(v)).
Für jeden von v aus erreichbaren Knoten w, d.h. v
wird dfs(w) genau einmal aufgerufen.
5
2
2
3
7→
1
2
1
2
2
2
3
7→
3
1
4
2
1
1
1
2
3
Tiefensuche in Digraphen – 8
=⇒ Aufruf dfs(vr−1) und irgendwann wird w ∈ suc(vr−1)
entdeckt. Also wird dfs(w) aufgerufen, Widerspruch!
10
9
AuD – 11.07.2006
28
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
29
Tiefensuche in Digraphen – 9
Tiefensuche in Digraphen – 10
Def. Für jeden Knoten v in einem Digraphen G = (V, E)
bezeichne Rv (für reachable) die Menge der von v aus erreichbaren Knoten, d.h.
Bem. Zu jedem v ∈ V gehört also ein dfs-Aufrufbaum Tdfs(v)
mit Wurzel v, der Tiefensuchbaum zu v.
Seien v1, . . . , vl die Nachfolger von v, deren Status bei Aufruf
dfs(v) noch neu ist, und seien vi1 , . . . , vik mit i1 < . . . < ik
diejenigen Nachfolger mit
Rv := {w ∈ V | v
G
w}
Bem. Jeder Knoten w ∈ Rv \ {v} wird nach Lemma, in
Abhängigkeit von der Reihenfolge der Nachfolger v1, . . . , vl(v),
l(v) := outdeg(v), von v in der Adjazenzliste für v, von einem
eindeutig bestimmten Knoten aus entdeckt, in Zeichen p(w).
vij+1 ∈
/ Tdfs(vi1 ) ∪ . . . ∪ Tdfs(vi
AuD – 11.07.2006
30
)
für j = 1, . . . , k−1.
Dann besitzt der Tiefensuchbaum zu v die folgende Gestalt:
Der Aufruf dfs(w) erfolgt während der Abarbeitung von
Aufruf dfs(p(w)). Insbesondere: dfs(w) beginnt nach Aufruf
dfs(p(w)) und endet vor Ende des Aufrufs dfs(p(w)).
FG KTuEA, TU Ilmenau
j
Tdfs(v):
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
31
Tiefensuche in Digraphen – 11
Tiefensuche in Digraphen – 12
Bsp. Tiefensuchbaum im Graphen (mit Pfeilen von p(w) zu
w, die Entdeckungsrichtung) und Tdfs(v0) mit v0 := 1.
1
4
1
1
2
2 1
2
6
6
1
1
2
8
4
1
4
2
1
7
5
1
2
3
2
3
3
2
9
die Menge der Kanten, die aus Knoten in Rv ausgehen.
Bem. Es gilt |Rv | ≤ |Ev |+1.
3
Lemma (Beobachtung 2 zu dfs(v)).
Ein Aufruf dfs(v) benötigt Zeit O(|Ev |), d.h. ist linear in |E|.
10
Satz (Charakterisierung von Tdfs(v)).
w ∈ Tdfs(v) ⇐⇒ bei Aufruf dfs(v) existiert ein Weg v
aus Knoten mit Status neu.
Beweis. Induktion über #(rekursive Aufrufe von dfs).
FG KTuEA, TU Ilmenau
Ev := {(u, w) ∈ E | u ∈ Rv }
5
2
1
Def. Für einen Knoten v (in G) bezeichne
AuD – 11.07.2006
G
w
Beweis. Der Zeitaufwand für die Bearbeitung eines Aufrufs
dfs(v) ohne die rekursiven Aufrufe ist O(1+outdeg(v)). Also
benötigt Aufruf dfs(v) insgesamt:
P
O
1+outdeg(w) = O(|Ev |) w∈Rv
32
FG KTuEA, TU Ilmenau
Tiefensuche in Digraphen – 13
Bew. O(|V |) sind Initialisierungskosten. Nach Aufruf dfs(v)
kann man über eine Liste für Rv in Zeit O(|Rv |) das statusArray wieder auf neu setzen. Def. Die transitive Hülle eines Digraphen G = (V, E) ist der
Digraph TH(G) := (V, TH(E)) mit
G
w}.
AuD – 11.07.2006
Bem. Auch Breitensuche leistet dies.
2. Globale Tiefensuche in G:
1: procedure global-dfs(G)
2:
dfs count ← 0
3:
for v ← 1 to n do
4:
status[v] ← neu
5:
6:
7:
for v ← 1 to n do
if status[v] = neu then
dfs(v)
⊲ Status-Initialisierung
⊲ v noch nicht erreicht!
⊲ Starte dfs von v aus
Bem. dfs-Aufrufe verändern die status-Werte. Durch Zeilen
6-7 werden insgesamt alle Knoten in G erreicht!
Folg. TH(E) ist in Zeit O(|TH(E)|) berechenbar.
FG KTuEA, TU Ilmenau
33
Tiefensuche in Digraphen – 14
Folg (Strukturinformation 1). Mit Tiefensuche kann man
• in Zeit O(|V |+|Ev |) jede Menge Rv ermitteln, P
• nacheinander für jeden Knoten, in Zeit O(|V |+ v∈V |Ev |)
alle Mengen Rv ermitteln.
TH(E) := {(v, w) | v, w ∈ V, v
AuD – 11.07.2006
34
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
35
Tiefensuche in Digraphen – 15
Tiefensuche in Digraphen – 16
Lemma (Laufzeit). global-dfs(G) für einen Digraphen
G = (V, E) benötigt Zeit O(|V |+|E|), d.h. Linearzeit!
Bew. Ein Aufruf dfs(v) benötigt Zeit O(1+outdeg(v)) und
jeder Knoten wird genau einmal besucht. Gesamtzeit:
P
O
1+outdeg(v) = O(|V | + |E|) v∈V
Bsp. Alle Tiefensuchbäume mit dfs-Nummern
4
1
1
1
2
2 1
2
6
6
1
1
2
3
2
3
3
7
4
1
2
2
1
1
7
5
2
FG KTuEA, TU Ilmenau
9
5
3
2
8
4
1
10
9
8
10
AuD – 11.07.2006
36
Strukturinformation 2: Kreisfreiheitstest
Durch Erweiterung der Prozedur dfs(v) um ein globales Flagbit kreis gesehen (mit false initialisiert) erhält man einen
Linearzeittest auf Kreisfreiheit:
⊲ Tiefensuche von v aus, rekursiv
procedure dfs-e(v)
...
foreach w ∈ suc(v) do
⊲ nach Adjazenzliste von v
if status(w) = aktiv then
⊲ w Kreis entdeckt!
kreis gesehen ← true
if status(w) = neu then
⊲ w wird entdeckt
dfs-e(w)
...
FG KTuEA, TU Ilmenau
Tiefensuche in Digraphen – 17
4
1
1
2
2
1
Bsp.
2
6
1
3
1
2
3
1
7
1
2
8
1
4
2
2
1
Tiefensuche in Digraphen – 18
5
3
Satz
=⇒ v
Satz
=⇒ v
10
Im Aufruf dfs−e(1) wird dfs−e(2) aufgerufen und Kante (2, 1)
inspiziert. Knoten 1 ist noch aktiv, Kreis wird entdeckt!
Satz (Kreisfreiheit).
1. Rv enthält einen Kreis ⇐⇒ nach Aufruf dfs-e(v) gilt
kreis gesehen = true.
2. G enthält einen Kreis ⇐⇒ nach global-dfs-e(G) gilt
kreis gesehen = true.
AuD – 11.07.2006
u →G w, dfs−e(w) wurde vor dfs−e(u) aufgerufen.
G w
G u, fertig.
G
=⇒ Im Aufruf dfs − e(v) numeriere (in Gedanken) mit
f num(w) ∈ {1, . . . , |V |} die Knoten w in der Reihenfolge
der Beendigung der Aufrufe dfs−e(w).
6
4
1
1
Bsp. mit f-Nummern
5
1
1
3
2
1
2
8
3
10
1
4
2
2
1
7
1
2
3
2
8
1
4
2
2
6
Beweis. Aussage 2.) folgt aus Aussage 1.) Zu 1).
FG KTuEA, TU Ilmenau
37
⇐= Gelte kreis gesehen = true nach Aufruf dfs-e(v).
=⇒ ∃u ∈ Tdfs−e(v) ∃w ∈ suc(u) : status[w] = aktiv
2
9
AuD – 11.07.2006
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
3
2
1
9
10
7
9
Es gilt: u →G w =⇒ f num(u) > f num(w). Fertig.
38
5
39
Topologische Sortierung in Digraphen– 1
Topologische Sortierung in Digraphen – 2
Strukturinformation 3: Topologische Sortierung
global-dfs findet eine topologische Sortierung!
Def. Eine topologische Sortierung eines azyklischen Digraphen G ist eine Bijektion f : V → {1, . . . , n} mit:
Idee: Wissen: Die f-Nummern sind strikt fallend entlang
Kanten. Definiere daher (mit n := |V |):
∀(v, w) ∈ E(G) : f (v) < f (w)
f (v) := (n+1) − f num(v)
Zur Berechnung der f-Nummern muß man nur dfs-e geringfügig erweitern.
Sortierung der Knoten: f −1(1), f −1(2), . . . , f −1(n).
6
4
1
1
Bsp. mit f-Nummern
2
1
3
5
1
7
2
1
2
3
10
1
4
2
2
1
6
8
1
4
2
Folg (Topologisches Sortieren in Digraphen).
Die Berechnung einer topologischen Sortierung für azyklische
Digraphen G = (V, E) benötigt Linearzeit:
5
3
1
2
8
3
1
9
10
7
9
O(|V |+|E|)
mit topologischer Sortierung: 5, 10, 4, 9, 1, 6, 2, 8, 7, 3
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
40
Topologische Sortierung in Digraphen – 3
Bem. dfs-ts-Aufrufe verändern die f count-Werte.
AuD – 11.07.2006
AuD – 11.07.2006
41
Tiefensuche in ungerichteten Graphen – 1
Beendete Aufrufe mitzählen: in f count, mit 0 initialisiert.
1: procedure dfs-ts(v) ⊲ Tiefensuche von v aus, rekursiv
2:
dfs count ← bfs count+1
3:
dfs num(v) ← dfs count
4:
status(v) ← aktiv
5:
foreach w = suc(v) do
⊲ nach Adjazenzliste von v
⊲ w wird entdeckt
6:
if status(w) = neu then
dfs-ts(w)
7:
8:
f count ← f count+1
9:
f num(v) ← f count
10:
status(v) ← fertig
FG KTuEA, TU Ilmenau
FG KTuEA, TU Ilmenau
Im folgenden sei G = (V, E) ein ungerichteter Graph.
Erinnerung: 1) Für Knoten u, v ∈ V schreiben wir u ∼G v,
falls u mit v durch einen Weg in G verbunden ist.
2) ∼G ist eine Äquivalenzrelation auf V .
3) [v]∼G := {u | u ∼G v} heißt Äquivalenzklasse (Zusammenhangskomponente) von G mit Repräsentant v.
Ziele:
• Zusammenhangskomponenten finden
• Spannbaum für jede Zusammenhangskomponente finden
• Kreisfreiheitstest
Bem. Diese Aufgaben kann auch Breitensuche erledigen.
42
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
43
Tiefensuche in ungerichteten Graphen – 2
Tiefensuche in ungerichteten Graphen – 3
Input: Knoten v mit status(v) = neu.
1: procedure udfs(v)
⊲ Tiefensuche von v aus, rekursiv
2:
dfs count ← dfs count+1
3:
dfs num(v) ← dfs count
4:
status(v) ← aktiv
5:
foreach Nachbar w von v do
⊲ Adjazenzliste!
⊲ w wird entdeckt
6:
if status(w) = neu then
udfs(w)
7:
8:
status(v) ← fertig
Tiefensuche von v0 aus: Initialisiere dfs count mit 0 und
den Status aller Knoten mit neu. Rufe udfs(v0) auf.
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
44
Tiefensuche in ungerichteten Graphen – 4
5:
7:
for v ← 1 to n do
if status[v] = neu then
udfs(v)
2. Bei Aufruf udfs(v) gilt: Die Kanten (w, u), wo udfs(u)
unmittelbar aus udfs(w) aufgerufen wird, bilden einen (gerichteten) Baum mit Wurzel v, in Zeichen Tudfs(v).
Dies ist der Spannbaum von [v]∼G .
3. w ∈ Tudfs(v) ⇐⇒ bei Aufruf udfs(v) existiert ein Weg
v ∼G w aus Knoten mit Status neu.
4. Die Laufzeit (mit Initialisierung) eines Aufrufs udfs(v) beträgt O(|V | + |Ev |), wobei Ev die Menge der Kanten in
[v]∼G ist.
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
45
Tiefensuche in ungerichteten Graphen – 5
Globale Tiefensuche in (ungerichtetem) Graphen G:
1: procedure global-udfs(G)
2:
dfs count ← 0
3:
for v ← 1 to n do
4:
status[v] ← neu
⊲ Status-Initialisierung
6:
Satz (Tiefensuche bei ungerichteten Graphen).
1. Aufruf udfs(v) entdeckt genau die Knoten in [v]∼G .
⊲ v noch nicht erreicht!
⊲ Starte udfs von v aus
Kreisfreiheitstest in ungerichteten Graphen.
Zudem: Finden eines Kreises, wenn es einen gibt.
Erinnerung: Ein Kreis in G ist ein Weg (v0, v1, . . . , vk ) mit
k ≥ 3, v0 = vk , ∀i ∈ {1, . . . , k} : vi−1 6= vi+1 sowie vk−1 6= v1.
Ein Hin und her“ (. . . , u, v, u, . . . ) über dieselbe Kante
”
(u, v) = (v, u) ist also nicht erlaubt!
Wir merken uns die Vorgänger in einem Array p[1..n], das mit
Nullen initialisiert wird.
Satz. Der Aufruf global-udfs(G) erzeugt eine Reihe von
Bäumen Tudfs(v). Deren Knotenmengen sind genau die ZHKn
[v]∼G von G. Die Laufzeit ist linear: O(|V |+|E|)
Durch Erweiterung der Prozedur udfs(v) um ein globales
Flagbit kreis gesehen (mit false initialisiert) erhält man einen
Linearzeittest auf Kreisfreiheit:
FG KTuEA, TU Ilmenau
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
46
AuD – 11.07.2006
47
Tiefensuche in ungerichteten Graphen – 6
1:
2:
3:
4:
5:
6:
7:
8:
9:
Tiefensuche in ungerichteten Graphen – 7
procedure udfs-e(v) ⊲ Tiefensuche von v aus, rekursiv
...
foreach Nachbar w von v do
⊲ Adjazenzliste!
if status(w) = aktiv ∧ w 6= p[v] then
kreis gesehen ← true
if status(w) = neu then
⊲ w wird entdeckt
p[w] ← v
⊲ Vorgänger eingetragen!
udfs-e(w)
...
Viel Erfolg in der Klausur!
Schöne Ferien!
Satz. G enthält einen Kreis ⇐⇒ nach global-dfs-e(G)
gilt kreis gesehen = true. Wenn Kante (v, w) wie in Zeile 4-5
gefunden wird, so bildet diese mit w ∼G v einen Kreis. FG KTuEA, TU Ilmenau
AuD – 11.07.2006
48
A&D-Klausur: Relevant sind
1. Definitionen von grundlegenden Begriffen/Konzepten:
• Lernen und knapp, aber präzise hinschreiben können
Bsp. Kreise in ungerichteten Graphen
2. Zentrale Resultate der Vorlesung:
• Vestehen, lernen und spielerisch damit umgehen können
(wichtig für Fragenkatalog!)
3. Beweise bzw –skizzen/Ideen (Konstruktionen) dazu:
• Verstehen und knapp, aber präzise hinschreiben können
Bsp. Satz Untere Schranke
4. Wichtige Aufgabentypen im Stil der Übungen
• Grundlegende Techniken (z.B. Mastertheorem) bzw. Algorithmen etc. beherrschen und anwenden können
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
50
FG KTuEA, TU Ilmenau
AuD – 11.07.2006
49
Herunterladen