Teil III: Graphenalgorithmen

Werbung
Teil III: Graphenalgorithmen
! 
! 
! 
! 
! 
! 
! 
! 
! 
Anwendungen
Definitionen
Datenstrukturen für Graphen
Elementare Algorithmen
Topologisches Sortieren
Kürzeste Wege
Minimal aufspannende Bäume
Flüsse in Netzwerken
Zusammenhangskomponenten
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-1
Kommunikationsnetze (1)
Menge von Rechnern, die mit Leitungen verbunden sind.
Beispiele
Knoten
(Rechner)
Kante
(Leitung)
Vollständig vernetzte Struktur
Ringstruktur
Prof. Dr. O. Bittel, HTWG Konstanz
Sternstruktur
Busstruktur
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-2
Kommunikationsnetze (2)
Zuverlässigkeit von Kommunikationsnetzen
!  lässt sich bemessen durch:
!  Verbindungszusammenhang:
Minimale Anzahl von Verbindungen, bei deren Ausfall die Kommunikation
zwischen irgendwelchen Knotenpaaren unterbrochen wird.
!  Knotenzusammenhang:
Minimale Anzahl von Knoten, bei deren Ausfall die Kommunikation zwischen
irgendwelchen Knotenpaaren unterbrochen wird.
!  Algorithmus → Flüsse in Netzwerken.
Routing-Problem
!  Bestimme von einem Rechnerknoten den günstigsten Weg zum Zielknoten.
Die Güte einer Verbindungskante ist dabei beispielsweise durch seine
Paketkapazität definiert.
!  Algorithmus → kürzeste Wege in Graphen
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-3
Routenplanung
Problemstellung
!  Finde in einem Straßennetz die kürzeste Verbindung (Länge bzw. Fahrtzeit)
von einer Stadt A zu einer Stadt B.
1
6
7
1
8
8
3
1
3
6
2
0
4
4
1
2
2
7
1
1
4
5
!  Algorithmus → kürzeste Wege in Graphen
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-4
Netzplanung
Problemstellung
!  Zwischen n Orten soll ein Versorgungsnetz (Kommunikation, Strom, Wasser, etc.)
aufgebaut werden, so dass je 2 Orte direkt oder indirekt miteinander verbunden sind.
Die Verbindungskosten zwischen 2 Orte seien bekannt.
!  Gesucht ist ein Versorgungsnetz mit den geringsten Kosten.
7
2
1
2
1
5
6
1
2
4
2
4
1
2
2
Orte mit Verbindungskosten
Günstigstes Versorgungsnetz
!  Algorithmus → minimal aufspannende Bäume
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-5
Zyklenfreiheit bei gerichteten Graphen (1)
!  Beispiel: Ausschnitt aus Vererbungsgraph für stream-Klassen in C++
ios
istream
ostream
iostream
fstream
strstream
!  Beispiel: include-Graph in C
mod.h
Dictionary.h
set.h
Prof. Dr. O. Bittel, HTWG Konstanz
string.h
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-6
Zyklenfreiheit bei gerichteten Graphen (2)
!  Beispiel: Montageanleitung für Schrank
Füße an Boden
montieren
Linke
Seitenwand
montieren
Rechte
Seitenwand
montieren
Decke
montieren
Scharniere
an Türen
montieren
Linke Türe an linke
Seitenwand
montieren
Rechte Türe an
rechte Seitenwand
montieren
!  Algorithmus → topologische Sortierung
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-7
Internet-Suchmaschinen (1)
!  Web-Seiten mit ihren Verweisen
A
C
B
D
!  Problemstellung: Indexierung
Besuche alle Internetseiten, extrahiere Schlüsselwörter
und speichere diese in einen Index.
!  Algorithmus → Tiefensuche
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-8
Internet-Suchmaschinen (2)
Problemstellung: Relevanz von Internetseiten
!  Bei Eingabe eines Suchbegriffs durchsuchen die Suchmaschinen ihren Index.
Die gefundenen Internetseiten werden üblicherweise nach Relevanz sortiert
ausgegeben.
!  Problem: Stelle die Relevanz von Internetseiten fest.
PageRank-Algorithmus von Page und Prin (s. [Turau])
1)  Berechne für jede Internetseite w den PageRank r(w) nach folgender Formel:
l(v) = Anzahl der
ausgehenden Links von v
α ∈ [0,1] ist Dämpfungsfaktor
v1
…
w
vn
2)  Wiederhole Schritt 1) solange, bis r(w) für alle Seiten hinreichend konvergiert.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-9
Internet-Suchmaschinen (3)
Beispiel:
!  Für das Beispiel-Netz ergeben sich
mit α = 0.5 folgende Gleichungen:
r(A) = 0.5 + 0.5*r(B)
r(B) = 0.5 + 0.5*r(A)/2
r(C) = 0.5 + 0.5*(r(A)/2 + r(D))
r(D) = 0.5 + 0.5*r(C)
A
C
B
D
!  Beginnt man mit den Initialisierungswerten r(w) = 1 und
wendet man die Gleichungen iterativ an,
ergeben sich bereits nach 4 Iterationen stabile Werte:
Iterationen
r(A)
r(B)
r(C)
r(D)
1
1
1
1
1
2
1
0.75
1.25
1.0625
3
0.875
0.71875
1.071875
1.0359375
4
0.859375
Prof. Dr. O. Bittel, HTWG Konstanz
0.71484375 1.072265625 1.0361328125
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-10
Teil III: Graphenalgorithmen
! 
! 
! 
! 
! 
! 
! 
! 
! 
Anwendungen
Definitionen
Datenstrukturen für Graphen
Elementare Algorithmen
Topologisches Sortieren
Kürzeste Wege
Minimal aufspannende Bäume
Flüsse in Netzwerken
Zusammenhangskomponenten
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-11
Gerichteter Graph
!  Ein gerichteter Graph G = (V, E) besteht aus einer Menge V von Knoten (engl. vertices)
und einer Menge E ⊆ V×V von Kanten (engl. edges).
!  Eine Kante ist ein geordnetes Paar von Knoten (v,w).
!  Die Kanten sind gerichtet: die Kante (v,w) geht von Knoten v nach Knoten w.
In den graphischen Darstellungen werden Pfeile verwendet.
!  Kanten der Form (v,v) heißen Schlingen.
!  Gerichtete Graphen werden auch Digraphen genannt.
v3
v4
v2
v1
v5
V = { v1 , v2 , v3 , v4 , v5 }
E = { (v1,v2), (v2,v3), (v3,v1), (v3,v4), (v4,v2), (v4,v5) }
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-12
Ungerichteter Graph
!  Bei einem ungerichteter Graph G = (V, E) haben die Kanten keine Richtung.
!  Eine Kante ist gegeben durch ein ungeordnetes Paar von Knoten (v,w).
Es gilt (v,w) = (w,v).
!  Ein ungeordnetes Paar von Knoten (v,w) ist dasselbe wie die 2-elementige Menge
von Knoten {v, w}. Es muss v ≠ w sein (keine Schlingen!).
!  Die Darstellung soll jedoch einfach bleiben. Daher werden wir sowohl für
gerichtete als auch ungerichtete Kanten dieselbe Notation (v,w) verwenden.
v3
Knoten
v4
v2
Kante
v1
v5
V = { v1 , v2 , v3 , v4 , v5 }
E = { (v1,v2), (v1,v3), (v2,v3), (v2,v4), (v3,v4), (v5,v4) }
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-13
Adjazenz und Grad
!  Ein Knoten w ist adjazent zu (benachbart zu)
einem Knoten v, falls (v,w) eine Kante des
(gerichteten bzw. ungerichteten) Graphen ist.
v
w
v
w
!  Bei einem ungerichteten Graphen ist die Adjazenzbeziehung immer symmetrisch.
Bei einem gerichteten Grahen ist die Adjazenzbeziehung im allg. unsymmetrisch.
!  Bei einer gerichteten Kante (v,w) wird w auch Nachfolger von v und
v Vorgänger von w genannt.
Bei einer ungerichteten Kante (v,w) heißen v und w auch Nachbarn.
!  In einem ungerichteten Graphen ist der Grad eines Knoten die Anzahl seiner Nachbarn.
!  Bei einem gerichteten Graphen wird die Anzahl der Vorgänger Eingangsgrad
und die Anzahl der Nachfolger Ausgangsgrad genannt.
v
v
-  Der Knoten v hat 5 adjazente
Knoten.
-  Der Knoten v hat 2 Vorgänger
und 3 Nachfolger.
-  Der Grad von v ist damit 5.
-  Der Eingangsgrad von v ist damit 2 und
der Ausgangsgrad ist 3.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-14
Weg
!  Ein Weg (engl. path) ist eine Folge von Knoten v1, v2, …, vn,
wobei n ≥ 1 und (vi, vi+1) für 1 ≤ i < n Kanten im Graphen sind.
!  Die Länge des Weges v1, v2, …, vn ist gleich der Anzahl der Kanten und damit n-1.
n darf 1 sein; dann hat der Weg die Länge 0.
!  Ein Weg heißt einfacher Weg, falls alle Knoten bis auf Anfangs- und Endknoten
unterschiedlich sind.
v3
v3
v4
v2
v1
v5
v1
Ein einfacher Weg
der Länge 3: v1, v2, v3, v4.
Prof. Dr. O. Bittel, HTWG Konstanz
v4
v2
v5
Ein (nicht einfacher) Weg der
Länge 5: v1, v2, v5, v4, v2, v3
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-15
Zyklus
!  Ein Weg v1, v2, …, vn in einem gerichteten Graph heisst Zyklus (cycle),
falls Anfangsknoten v1 und Endknoten vn identisch sind
und n ≥ 2 ist (d.h. wenigstens eine Kante).
!  Ein Weg v1, v2, …, vn in einem ungerichteten Graph heisst Zyklus (cycle),
falls Anfangsknoten v1 und Endknoten vn identisch sind,
n ≥ 4 ist (d.h. wenigstens drei Kanten) und alle Kanten unterschiedlich sind.
!  Zyklen sind einfach, wenn der Weg einfach ist.
!  Zwei Zyklen sind gleich, falls sich ihre Wege (jeweils ohne Endknoten) durch eine
zyklische Verschiebung unterscheiden.
!  Zyklenfreie Graphen werden auch azyklisch (acyclic graph) genannt.
v3
v3
v1
v2
v3
Ein einfacher Zyklus
(in einem gerichteten Graph)
der Länge 3:
v1 , v 2 , v 1
Prof. Dr. O. Bittel, HTWG Konstanz
v4
v2
v1
v5
Ein einfacher Zyklus
der Länge 3:
v1 , v 2 , v 3 , v 1
v4
v2
v1
v5
Ein nicht-einfacher Zyklus
der Länge 6:
v1 , v 2 , v 3 , v 1 , v 5 , v 4 , v 1
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-16
Teilgraph
!  Ein Graph G' = (V', E') ist ein Teilgraph von Graph G = (V, E),
falls V' ⊆ V und E' ⊆ E
!  Enthält darüber hinaus G' alle Kanten aus E, die Knoten aus V' verbinden,
dann heißt G' der durch V' induzierte Teilgraph von G, d.h.
E' = {(u,v) ∈ E / u,v ∈ V'}
v5
v3
v4
v3
v4
v3
v4
v1
v2
v1
v2
v1
v2
Graph G
Prof. Dr. O. Bittel, HTWG Konstanz
Teilgraph von G
Der durch V = {v1, v2, v3, v4}
induzierte Teilgraph von G
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-17
Zusammenhang
!  Ein ungerichteter Graph heißt zusammenhängend (connected), falls es
von jedem Knoten einen Weg zu jeden anderen Knoten gibt.
!  Eine Zusammenhangskomponente eines ungerichteten Graphen G ist ein
maximal zusammenhängender Teilgraph.
v
Graph G = (E,V) ist
zusammenhängend.
Prof. Dr. O. Bittel, HTWG Konstanz
Der durch V \ {v} induzierte Teilgraph
(d.h. G ohne Knoten v und ohne Kanten
mit v als Endknoten) besteht aus genau zwei
Zusammenhangskomponenten.
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-18
Strenger und schwacher Zusammenhang
!  Ein gerichteter Graph heißt streng zusammenhängend (strongly connected), falls es
von jedem Knoten einen Weg zu jedem anderen Knoten gibt.
!  Wenn bei einem gerichteter Graph G der entsprechende ungerichtete Graph
zusammenhängend ist, dann wird G auch schwach zusammenhängend genannt.
(weakly connected).
Gerichteter Graph, der schwach aber
nicht stark zusammenhängend ist.
!  Strenge und schwache Zusammenhangskomponenten werden analog zu
Zusammenhangskomponenten bei ungerichteten Graphen definiert.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-19
Wald und Baum
!  Ein ungerichteter und azyklischer Graph heißt auch Wald.
!  Ein zusammenhängender Wald heißt auch Baum.
Wald
Baum
!  Ein Wurzelbaum ist ein gerichteter, azyklischer Graph, bei dem genau ein Knoten
– die sogenannte Wurzel – den Eingangsgrad 0 hat und alle anderen Knoten den
Eingangsgrad 1 haben.
Bei einer Kante (v,w) heisst v auch Elternknoten von w und w Kindknoten von v.
Knoten ohne Kinder heißen auch Blätter.
In der graphischen Darstellung zeigen die Kanten üblicherweise nach unten.
Die im Kap. 1 eingeführten Suchbäume sind Wurzelbäume.
Wurzel
v
w
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-20
Gewichteter Graph
!  Ist jeder Kante (v,w) eine reelle Zahl c(v,w) als Kosten oder Gewicht zugeordnet,
spricht man von einem gewichteten Graphen.
!  Sind darüber hinaus die Gewichte ≥ 0, dann heißt der Graph auch Distanzgraph.
7
2
1
2
1
5
6
2
4
2
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-21
Teil III: Graphenalgorithmen
!  Anwendungen
!  Definitionen
!  Datenstrukturen für Graphen
- 
- 
- 
- 
! 
! 
! 
! 
! 
! 
Adjazenzmatrix
Adjazenzliste
Kantenliste
Implementierungshinweise für Java
Elementare Algorithmen
Topologisches Sortieren
Kürzeste Wege
Minimal aufspannende Bäume
Flüsse in Netzwerken
Zusammenhangskomponenten
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-22
Adjazenzmatrix für ungewichtete Graphen
A[i][j] =
1, falls es eine Kante von vi und nach vj gibt
0 sonst
v1
v1
v2
v0
v2
v0
v4
v3
[0] [1] [2] [3] [4]
v4
v3
[0] [1] [2] [3] [4]
[0] 0
1
0
0
1
[0] 0
1
0
0
0
[1] 1
0
1
0
1
[1] 0
0
1
0
1
[2] 0
1
0
1
1
[2] 0
0
0
1
1
[3] 0
0
1
0
1
[3] 0
0
0
0
0
[4] 1
1
1
1
0
[4] 1
0
0
1
0
Beachte: Die Adjazenzmatrizen sind bei einem ungerichteten Graphen symmetrisch
und bei einem gerichteten Graphen im allgemeinen unsymmetrisch.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-23
Adjazenzmatrix für gewichtete Graphen
A[i][j] =
c(v,w), falls es eine Kante von Knoten v nach w gibt
∞ sonst
v1
v1
v2
7
2
2
v0
v2
7
6
1
3
v0
6
1
3
4
4
v4
v3
-3
[0] [1] [2] [3] [4]
-3
v4
v3
[0] [1] [2] [3] [4]
[0] ∞
2
∞ ∞
4
[0] ∞
[1] 2
∞
7
∞
1
[1] ∞ ∞
7
∞
1
[2] 0
7
∞
3
6
[2] ∞ ∞ ∞
3
6
[3] ∞ ∞
3
∞ -3
[4] 4
6 -3 ∞
1
2
∞ ∞ ∞
[3] ∞ ∞ ∞ ∞ ∞
[4] 4
∞ ∞ -3 ∞
Beachte: Die Adjazenzmatrizen sind bei einem ungerichteten Graphen symmetrisch
und bei einem gerichteten Graphen im allgemeinen unsymmetrisch.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-24
Adjazenzliste
!  bei einem ungerichteten Graphen wird für jeden Knoten eine linear verkettete Liste
mit allen Nachbarknoten (Adjazenzliste) abgespeichert.
!  ungerichtete Kanten werden bei beiden Endknoten eingetragen.
v1
v2
v0
v4
v3
[0]
1
4
[1]
0
2
4
[2]
1
3
4
[3]
2
[4]
0
1
2
!  Bei einem gerichteten Graphen werden zwei getrennte Listen verwaltet:
eine Vorgänger-Liste und eine Nachfolger-Liste.
!  Bei einem gewichteten Graphen wird noch zusätzlich das Kantengewicht eingetragen.
!  Um die Dynamisierbarkeit der Datenstruktur zu verbessern, kann statt eines Feldes
auch eine dynamische Datenstruktur wie ein binärer Suchbaum verwendet werden.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-25
Kantenliste
!  Speichere die Menge aller Kanten in einer sortierten Suchstruktur
(sortiertes Feld oder Suchbaum).
!  Als Ordnungsrelation auf Kanten ist eine lexikographische Ordnung naheliegend:
(v, v') ≤ (w, w') falls v < w oder v = w und v' ≤ w'
!  Beispiel: sortiertes Kanten-Feld
v1
v2
[0] [1] [2] …
v0
v4
v3
[11]
i
0
0
1
1
1
2
2
2
3
4
4
4
j
1
4
0
2
4
1
3
4
2
0
1
2
Alle Nachbarknoten zu v2
sind effizient auffindbar.
!  Bei einem ungerichten Graphen wird für eine Kante von v nach w sowohl
(v,w) als auch (w,v) gespeichert.
!  Bei einem gerichteten Graphen wird die Kantenliste
in Vorgänger- und Nachfolger-Liste aufgeteilt.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-26
Dünn- und dichtbesetzte Graphen
!  Falls |E| = O(|V|) (dünnbesetzter Graph; sparse graph), dann ist die Speicherung als
Adjazenzliste oder Kantenliste Speicherplatz sparender und effizienter in der Laufzeit.
!  Falls |E| = O(|V|2) (dichtbesetzter Graph; dense graph), dann ist die Speicherung als
Adjazenzmatrix Speicherplatz sparender und effizienter in der Laufzeit.
!  Vollständiger Graph (alle Knotenpaare
sind durch eine Kante verbunden) ist
dichtbesetzt.
!  Manhattan-Graph (Graph für Mahattan
mit Kreuzungen als Knoten und
Strassen als Kanten) ist dünnbesetzt.
!  Es gilt: |E| = |V|*(|V|-1)/2 = O(|V|2) .
!  Es gilt: |E| ≈ 2*|V| = O(|V|) .
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-27
Implementierungshinweise für Java: indizierter Zugriff (1)
!  Knoten sind durchnummeriert: 0, 1, 2, ..., n-1
!  Nehme Knotennummer als Index eines Feldes
!  Adjazenzmatrizen lassen sich als einfache zweidimensionale Felder realisieren:
int n = 10; // Anzahl der Knoten
int [ ][ ] adjcencyMatrix = new int[n][n];
adjcencyMatrix[1][2] = 1;
// Kante von 1 nach 2 eintragen
!  Adjazenzlisten lassen sich als Feld von Listen realisieren:
int n = 10; // Anzahl der Knoten
List<List<Integer>> adjacencyList = new ArrayList<>(n);
for (int i = 0; i < n; i++)
adjacencyList.add( new LinkedList<>() );
adjacencyList.get(1).add(2);
// Kante von 1 nach 2 eintragen
adjacencyList[0]
Statt die adjazenten Knoten in Listen zu speichern,
ließen sich sich auch Sets (bei ungewichteten Graphen)
oder Maps (bei gewichteten Graphen) einsetzen.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
[1]
2
...
[8]
[9]
SS 2017
3-28
Implementierungshinweise für Java: indizierter Zugriff (2)
!  In der Praxis kommt es häufig vor, dass die Graph-Knoten nicht von 0 bis n-1
nummeriert sind, sondern beispielsweise als Strings gegeben sind.
Stuttgart
90 km
Ulm
150km
München
!  Die Schlüssel können dann mit Hilfe einer Map (TreeMap oder HashMap)
in eine interne Nummerierung von 0 bis n-1 umgerechnet werden:
-  Stuttgart -> 0
-  Ulm -> 1
-  München -> 2
!  Mittels der internen Nummerierung lässt sich dann eines der indizierten
Zugriffsverfahren von der vorhergehenden Seite verwenden.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-29
Implementierungshinweise für Java: Zugriff mit einer Map (1)
!  Eine Map ordnet jedem Knoten eine Adjazenzliste zu.
!  Die Adjazenzlisten sind ebenfalls Maps und ordnen jedem adjazenten
Knoten ein Gewicht zu (bei ungewichteten Graphen könnten entweder die
Gewichte auf 0 oder 1 gesetzt werden oder aber eine Set verwendet
werden).
!  Knoten können vom Typ Integer (Knoten sind nummeriert)
oder von einem anderen Typ sein (z.B. String).
!  Maps können entweder HashMaps oder TreeMaps sein
(siehe Beispiel nächste Seite).
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-30
Implementierungshinweise für Java: Zugriff mit einer Map (2)
!  Beispiel:
int n = 7;
// Anzahl Knoten
Map<Integer, Map<Integer, Integer>> adjacencyList = new TreeMap<>();
for (int i = 0; i < n; i++)
adjacencyList.put(i, new TreeMap<>());
adjacencyList.get(4).put(3, 43); // Kante von 4 nach 3 mit Gewicht 43 eintragen
adjacencyList.get(4).put(6, 46); // Kante von 4 nach 6 mit Gewicht 46 eintragen
adjacencyList.get(4).put(2, 42); // Kante von 4 nach 2 mit Gewicht 42 eintragen
5
6
4
3
1
3
6
46
2
42
43
!  Graph als TreeMap
!  Mit Knoten als Schlüssel
und Adjazenzliste als
Nutzdaten
!  Die Adjazenzlisten sind
ebenfalls TreeMaps.
2
0
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-31
Darstellung der Algorithmen in Pseudo-Code (1)
!  Um einfach und von der konkreten Datenstruktur für Graphen unabhängig
zu bleiben, wird bei der Formulierung der Algorithmen folgender PseudoCode benutzt:
!  Ungerichtete und gerichtete Graphen:
for ( jeden adjazenten Knoten w von v ) {
…
}
for ( jeden Knoten v ) {
…
}
for ( jede Kante (v,w) ) {
…
}
!  Ungerichtete Graphen:
for ( jeden Nachbarn w von v ) {
…
}
!  Gerichtete Graphen:
for ( jeden Nachfolger w von v ) {
…
}
Prof. Dr. O. Bittel, HTWG Konstanz
for ( jeden Vorgänger w von v ) {
…
}
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-32
Darstellung der Algorithmen in Pseudo-Code (2)
!  Die hier in Pseudo-Code dargestellten Schleifen lassen sich in Java mit den
besprochenen Datenstrukturen (Adjazenzmatrix, Adjazenzliste und Kantenliste)
einfach realisieren.
Pseudo-Code:
Java:
...
for ( jeden Knoten v ) {
…
}
// Map-basierte Adjazenzliste für ungewichteten Graphen:
Map<Vertex, Set<Vertex>> adjacencyList = new TreeMap<>();
for ( jeden Nachfolger w von v ) {
…
}
// adjacencyList wie oben definiert
Prof. Dr. O. Bittel, HTWG Konstanz
for (Vertex v : adjacencyList.keySet() ) {
...
}
for (Vertex w : adjacencyList.get(v) ) {
...
}
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-33
Darstellung der Algorithmen in Pseudo-Code (3)
!  Bei der Formulierung der Algorithmen sollen auch die Datenstrukturen
möglichst einfach gehalten werden.
!  Oft muss für jeden Knoten eine Information gespeichert werden.
!  Im Pseudo-Code werden einfachheitshalber Felder und Indizierung
gewählt. Sind die Knoten durchnummeriert, dann kann der Pseudo-Code
direkt in Java übernommen werden.
Bei einem beliebigen Knotentyp kann eine Map (bzw. Set für ein boolsches
Feld) gewählt werden.
Pseudo-Code:
Java:
int[ ] inDegree;
...
for ( jeden Knoten v ) {
inDegree[v] = Anzahl der Vorgänger;
if (inDegree[v] == 0)
...
}
Map<Vertex, Integer> inDegree = new ...;
...
for ( jeden Knoten v ) {
inDegree.put(v, Anzahl der Vorgänger);
if (inDegree.get(v) == 0)
...
}
for ( jeden Nachfolger w von v ) {
if (--inDegree[w] == 0)
...
}
for ( jeden Nachfolger w von v ) {
inDegree.put(w, inDegree.get(w)-1);
if (inDegree.get(w) == 0)
...
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-34
Teil III: Graphenalgorithmen
! 
! 
! 
! 
Anwendungen
Definitionen
Datenstrukturen für Graphen
Elementare Algorithmen
-  Tiefensuche
-  Breitensuche
! 
! 
! 
! 
! 
Topologisches Sortieren
Kürzeste Wege
Minimal aufspannende Bäume
Flüsse in Netzwerken
Zusammenhangskomponenten
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-35
Rekursive Tiefensuche (depth-first search)
void visitDF(Vertex v, Graph g) {
boolean[ ] besucht;
besucht mit false initialisieren;
visitDF(v, g, besucht);
visitDF startet von Knoten v
eine Tiefensuche im Graph g.
Mit dem boolschen Feld besucht wird
gespeichert, ob ein Knoten bereits besucht
worden ist.
}
Wichtig zur Vermeidung von Endlosschleifen.
void visitDF(Vertex v, Graph g, boolean[ ] besucht) {
besucht[v] = true;
visitDF besucht Knoten v im Graphen g,
wobei die bereits besuchten Knoten im Feld
besucht abgespeichert sind.
visitDF ist rekursiv.
// Bearbeite v:
println(v);
for ( jeden adjazenten Knoten w von v )
if ( ! besucht[w]) // w noch nicht besucht
visitDF(w, g, besucht);
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-36
Beispiel für rekursive Tiefensuche
!  Tiefensuche mit Start bei Knoten 1
1
2
Besuchsreihenfolge:
Aufrufstruktur
1, 0, 4, 2, 3, 5
1
0
0
1
5
4
3
0
2
4
4
1
2
3
5
1
3
4
4
2
4
!  die Nachbarn eines Knoten werden in
lexikographischer Reihenfolge durchlaufen.
!  Bereits besuchte Nachbarknoten sind durch
eine gestrichelte Kante verbunden und
gehören nicht zur Aufrufstruktur.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-37
Tiefensuchbaum
!  Die Aufrufstruktur bildet mit den besuchten Knoten den sogenannten Tiefensuchbaum.
1
2
Tiefensuchbaum
Aufrufstruktur
1
0
0
1
5
4
3
0
4
0
4
4
1
2
3
5
1
3
4
4
2
Prof. Dr. O. Bittel, HTWG Konstanz
2
1
2
5
3
4
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-38
Iterative Tiefensuche mit einem Keller
void visitDF(Vertex v, Graph g) {
boolean[ ] besucht;
besucht mit false initialisieren;
visitDF(v, g, besucht);
}
visitDF startet von Knoten v eine Tiefensuche.
visitDF besucht Knoten v im Graphen g, wobei die
bereits besuchten Knoten im Feld besucht
abgespeichert werden.
void visitDF(Vertex v, Graph g, boolean[ ] besucht) {
Stack<Vertex> stk;
stk.push(v);
Die Tiefensuche wird erreicht durch die LIFOOrganisation des Kellers.
while( ! stk.empty() ) {
v = stk.pop();
if ( besucht[v] )
continue;
Um die gleiche Besuchsreihenfolge wie bei der
rekursiven Funktion zu erreichen, müssen in der forSchleife die Nachbarn in umgekehrter Reihenfolge
bearbeitet werden.
besucht[v] = true;
// Bearbeite v:
println(v);
}
for ( jeden adjazenten Knoten w von v )
if ( ! besucht[w] )
stk.push(w);
}
Prof. Dr. O. Bittel, HTWG Konstanz
Im Keller stk werden alle Knoten verwaltet, die als
nächstes zu besuchen sind.
Beachte: Gleiche Knoten können mehrfach
eingekellert werden. Das ließe sich durch eine
weitere Knotenmarkierung verhindern:
-  nicht besucht,
-  nicht besucht und im Keller,
-  besucht.
Allerdings würde sich eine andere Besuchsreihenfolge wie bei der rekursiven Funktion ergeben.
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-39
Breitensuche - Beispiel
2
1
0
5
2
4
3
0
4
1
5
3
1) Beginne mit Knoten 1.
2) Besuche alle Knoten, die über genau
eine Kante von Knoten 1 erreichbar sind.
2
Besuchsreihenfolge:
0
5
1
1, 0, 2, 4, 3, 5
4
3
3) Besuche alle Knoten, die über genau
2 Kanten von Knoten 1 erreichbar sind.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-40
Breitensuche (breadth-first search) mit einer Schlange
void visitBF(Vertex v, Graph g) {
boolean[ ] besucht;
besucht mit false initialisieren;
visitBF(v, g, besucht);
}
visitBF startet von Knoten v eine Breitensuche
im Graphen g.
void visitBF(Vertex v, Graph g, boolean[ ] besucht) {
Queue<Vertex> q;
q.add(v);
while( ! q.empty() ) {
v = q.remove();
if ( besucht[v] )
continue;
visitBF besucht Knoten v im Graphen g,
wobei die bereits besuchten Knoten im Feld
besucht abgespeichert werden.
In der Schlange q werden alle Knoten verwaltet,
die als nächstes zu besuchen sind.
Die Breitensuche wird erreicht durch die FIFOOrganisation der Schlange
besucht[v] = true;
// Bearbeite v:
println(v);
}
for ( jeden adjazenten Knoten w von v )
if ( ! besucht[w] )
q.add(w);
}
Prof. Dr. O. Bittel, HTWG Konstanz
Beachte: Gleiche Knoten können mehrfach in
die Schlange eingereiht werden.
Das ließe sich durch eine weitere
Knotenmarkierung verhindern:
-  nicht besucht,
-  nicht besucht und in der Schlange,
-  besucht.
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-41
Tiefen- bzw. Breitensuche bei mehreren Zusammenhangskomponenten
!  Hat ein Graph mehrere (strenge) Zusammenhangskomponenten, dann ist es nicht
garantiert, dass es von jedem Knoten v einen Weg zu jedem anderen Knoten w gibt.
!  Sollen alle Knoten besucht werden, dann muss visitDF (Tiefensuche) bzw. visitBF
(Breitensuche) mehrere Male mit allen noch nicht besuchten Knoten als Startknoten
aufgerufen werden.
void visitAllNodes() {
boolean[ ] besucht;
besucht mit false initialisieren;
for (jeden Knoten v)
if (! besucht[v] )
visit(v, g, besucht);
}
visit kann visitDF
oder visitBF sein.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-42
Tiefensuchwald bei Tiefensuche mit mehreren Zusammenhangskomponenten
!  Bei der Tiefensuche in einem Graphen mit mehreren Zusammenhangskomponenten
entsteht dann der sogenannte Tiefensuchwald.
void visitDFAllNodes() {
Gerichteter Graph (mit mehreren strengen
Zusammenhangskomponenten):
boolean[ ] besucht;
besucht mit false initialisieren;
1
3
2
for (jeden Knoten v)
if (! besucht[v] )
visitDF(v, g, besucht);
5
4
6
}
Tiefensuchwald (Knoten werden nach ihrer
Nummerierung aufsteigend besucht):
3
1
2
4
6
5
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-43
Analyse
!  Hier: Analyse der Tiefensuche über
den kompletten Graphen.
Bei der Breitensuche ergibt sich
dieselbe Komplexität.
void visitDFAllNodes() {
boolean[ ] besucht;
besucht mit false initialisieren;
for (jeden Knoten v)
if (! besucht[v] )
visitDF(v, g, besucht);
!  Jeder Knoten wird genau 1-mal
besucht.
!  Jede Kante wird genau 2-mal
bei ungerichteten Graphen bzw.
genau 1-mal bei gerichteten Graphen
in einer der for-Schleifen besucht.
Verwendet man die
Adjazenzlistendarstellung, ist damit
der Aufwand für den Durchlauf aller
for-Schleifen höchtens O(|E|).
!  Damit ergibt sich mit
Adjazenzlistendarstellung
insgesamt:
}
void visitDF(Vertex v, Graph g, boolean[ ] besucht) {
besucht[v] = true;
// Bearbeite v:
println(v);
for ( jeden adjazenten Knoten w von v )
if ( ! besucht[w]) // w noch nicht besucht
visitDF(w, g, besucht);
}
T = O(|V| + |E|)
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-44
Teil III: Graphenalgorithmen
! 
! 
! 
! 
! 
! 
! 
! 
! 
Anwendungen
Definitionen
Datenstrukturen für Graphen
Elementare Algorithmen
Topologisches Sortieren
Kürzeste Wege
Minimal aufspannende Bäume
Flüsse in Netzwerken
Zusammenhangskomponenten
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-45
Definition topologische Sortierung
!  Eine Folge aller Knoten eines gerichteten Graphen G
v0, v1, v2, …, vn-1
heißt topologische Sortierung, falls für alle Knoten u, v folgende Bedingung gilt:
falls es einen Weg von u nach v gibt, dann steht in der Folge u links von v.
!  Beispiel:
4
1
7
6
3
2
5
topologisch sortiert:
2
1
3
5
4
6
7
!  Anschaulich:
Unter Einhaltung der Adjazenzbeziehung lässt sich der Graph
so auf eine Linie „verbiegen“, dass die Pfeile nur nach rechts gehen.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-46
Eigenschaften und Anwendungen
Eigenschaften:
!  Falls der Graph einen Zyklus enthält, dann existiert keine topologische Sortierung.
1
4
2
3
!  Falls ein Graph topologisch sortiert werden kann, dann ist im allgemeinen die
topologische Sortierung nicht eindeutig.
Beispiele für Anwendungen:
!  Stelle fest, ob es eine Durchführungsreihenfolge für die Aktivitäten oder auch Prozesse
in einem Vorranggraphen gibt.
!  Prüfe, ob Vererbungsgraph oder include-Graph zyklenfrei ist.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-47
Algorithmus für topologische Sortierung
topSort liefert zu einem gerichteten
Graphen g eine topologische Sortierung.
List<Vertex> topSort(DiGraph g) {
List<Vertex> ts; // topologisch sortierte Folge
int[ ] inDegree; // Anz. noch nicht besuchter Vorgänger
Queue<Vertex> q;
for (jeden Knoten v) {
inDegree[v] = Anzahl der Vorgänger;
if (inDegree[v] == 0)
q.add(v);
}
while (! q.empty() ) {
v = q.remove();
ts.add(v);
for ( jeden Nachfolger w von v )
if (--inDegree[w] == 0)
q.add(w);
}
}
if (ts.size() != Anzahl Knoten in g)
return null;
// Graph zyklisch;
else
return ts;
Prof. Dr. O. Bittel, HTWG Konstanz
Idee:
!  Speichere für jeden Knoten v:
inDegree[v] = Anzahl der noch
nicht besuchten Vorgänger.
!  Speichere alle noch nicht besuchten
Knoten v, für die InDegree[v] == 0
gilt, in einer Schlange q.
q enthält also alle Knoten,
die als nächstes besucht
werden dürfen.
!  Besuche als nächstes den
vordersten Knoten
in der Schlange q
und entferne ihn aus q
!  Topologische sortierte Folge
wird in Liste ts abgespeichert.
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-48
Analyse
!  Für jeden Knoten w kann inDegree[w]
höchstens einmal gleich 0 werden.
Damit kommen in die Queue q
höchstens |V| Knoten.
Die while-Schleife läuft damit
höchstens |V|-mal.
List<Vertex> topSort(DiGraph g) {
List<Vertex> ts; // topologisch sortierte Folge
int[ ] inDegree; // Anz. noch nicht besuchter Vorgänger
Queue<Vertex> q;
for (jeden Knoten v) {
inDegree[v] = Anzahl der Vorgänger;
if (inDegree[v] == 0)
q.add(v);
}
!  Jede Kante wird höchstens einmal in
einer der for-Schleifen besucht.
Verwendet man die
Adjazenzlistendarstellung, ist damit
der Aufwand für den Durchlauf aller
for-Schleifen höchtens O(|E|).
while (! q.empty() ) {
v = q.remove();
ts.add(v);
for ( jeden Nachfolger w von v )
if (--inDegree[w] == 0)
q.add(w);
}
!  Damit ergibt sich mit
Adjazenzlistendarstellung
insgesamt:
T = O(|V| + |E|)
}
Prof. Dr. O. Bittel, HTWG Konstanz
if (ts.size() != Anzahl Knoten in g)
return null;
// Graph zyklisch;
else
return ts;
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-49
Teil III: Graphenalgorithmen
! 
! 
! 
! 
! 
! 
Anwendungen
Definitionen
Datenstrukturen für Graphen
Elementare Algorithmen
Topologisches Sortieren
Kürzeste Wege
- 
- 
- 
- 
- 
- 
Problemstellung
UngewichteteGraphenunderweiterteBreitensuche
DistanzgraphenundDijkstra-Algorithmus
GewichteteDigraphenundMoore-Ford-Algorithmus
NetzpläneunderweitertetopologischeSorCerung
AllekürzesteWegeingewichtetenDigraphenmitFloyd-Algorithmus
!  Minimal aufspannende Bäume
!  Flüsse in Netzwerken
!  Zusammenhangskomponenten
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-50
Problemstellung kürzeste Wege (1)
!  Im allgemeinen gibt es zwischen zwei Knoten u und v mehrere Wege.
!  Ein Weg von u nach v heißt kürzester Weg, falls der Weg minimale Länge hat.
!  Weglänge bei gewichtetem Graph ist Summe der Kantengewichte.
!  Weglänge bei ungewichtetem Graph ist Anzahl der Kanten.
!  Die Distanz d(u,v) zwischen u und v wird als die Länge eines kürzesten Weges
definiert. Falls es keinen Weg von u nach v gibt, ist d(u,v) = ∞.
1
6
7
1
8
8
3
1
3
6
2
0
1
2
4
Prof. Dr. O. Bittel, HTWG Konstanz
2
Weg von 4 nach 7 mit Länge 15.
4
7
Kürzester Weg von 4 nach 7 mit Länge 2.
Damit ist Distanz d(4,7) = 2.
1
1
4
5
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-51
Problemstellung kürzeste Wege (2)
!  Länge eines Weges muss nicht die wörtliche Bedeutung haben.
!  Die Gewichte (Kosten) der Kanten und damit die Weglängen können in
Abhängigkeit von der Anwendung ganz unterschiedliche Bedeutungen haben.
Beispiele:
-  Streckenlänge
-  Zeitspannen
-  Kosten
-  Profit: Gewinn/Verlust (Gewichte können positiv und negativ sein)
-  Wahrscheinlichkeiten
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-52
Verschiedene Problemvarianten
(1)  Single pair shortest path:
Kürzeste Wege zwischen zwei Knoten:
(2)  Single-source shortest-path problem:
Kürzeste Wege zwischen einem Knoten und allen anderen Knoten
(3)  All pairs shortest path:
Kürzeste Wege zwischen allen Knotenpaaren.
! 
Für Problem (1) kennt man keine bessere Lösung,
als einen Algorithmus für Lösung (2) zu nehmen,
der abgebrochen wird, sobald der Zielknoten erreicht wird.
! 
Problem (3) kann auf Problem (2) zurückgeführt werden.
Bei dicht besetzten Graphen (d.h. viele Kanten) gibt es jedoch
eine effizientere Lösung, die zudem auch negative Kantengewichte zulässt.
! 
Problemstellungen lassen sich für verschiedene Graphtypen (gerichtet oder
ungerichtet, gewichtet oder ungewichtet) algorithmisch unterschiedlich
lösen.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-53
Verschiedene Algorithmen
Problemvariante
Algorithmus
A
Single-source shortest-path für ungewichtete
Graphen
Erweiterte Breitensuche
B
Single-source shortest-path für Distanz-Graphen
(nur positive Gewichte)
Algorithmus von Dijkstra;
A*-Verfahren
C
Single-source shortest-path für gewichtete
Digraphen (auch negative Gewichten)
Algorithmus
von Moore und Ford
D
Single-source shortest-path für gewichtete,
azyklische Digraphen mit einem Start- und einem
Endknoten (Netzpläne)
Erweiterte topologische
Sortierung
E
All pairs shortest path für gewichtete Digraphen
(auch negative Gewichte)
Algorithmus von Floyd
!  A ist ein Spezialfall von B.
Wendet man den Dijkstra-Algorithmus auf Graphen mit Kantengewichten 1 an,
ergibt sich eine Breitensuche.
!  D ist ein Spezialfall von C.
Der auf Variante D zugeschnittene Algorithmus ist jedoch wesentlich effizienter
als der Algorithmus für Variante C.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-54
Teil III: Graphenalgorithmen
! 
! 
! 
! 
! 
! 
Anwendungen
Definitionen
Datenstrukturen für Graphen
Elementare Algorithmen
Topologisches Sortieren
Kürzeste Wege
- 
- 
- 
- 
- 
- 
Problemstellung
UngewichteteGraphenunderweiterteBreitensuche
DistanzgraphenundDijkstra-Algorithmus
GewichteteDigraphenundMoore-Ford-Algorithmus
NetzpläneunderweitertetopologischeSorCerung
AllekürzesteWegeingewichtetenDigraphenmitFloyd-Algorithmus
!  Minimal aufspannende Bäume
!  Flüsse in Netzwerken
!  Zusammenhangskomponenten
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-55
Erweiterte Breitensuche
!  Single-source shortest-path: Gesucht sind alle kürzesten Wege
von einem Startknoten s zu allen anderen Knoten.
!  Beginne bei s und durchlaufe Graph mit Breitensuche.
!  Speichere für jeden besuchten Knoten v die Distanz d[v] zu Startknoten s.
!  Speichere Vorgängerknoten p[v] im kürzesten Weg nach v.
p stellt ein Baum mit Wurzel s dar.
!  Der kürzeste Weg von s nach v ergibt sich in umgekehrter Reihenfolge:
v, p[v], p[p[v]], …, p[…p[p[v]]…] = s
Startknoten s = 4
Beispiel:
0
0
1
4
3
2
1
2
3
1
4
6
5
0
7
7
(1)
Prof. Dr. O. Bittel, HTWG Konstanz
4
6
5
2
6
5
7
(2)
(3)
3
v
0 1 2 3 4 5 6 7
d[v] ∞ ∞ ∞ ∞ 0 ∞ ∞ ∞
p[v] - - - - - - - -
(1)
v
0
d[v] ∞
p[v] -
1 2 3 4 5 6 7
1 1 ∞ 0 1 1 ∞
4 4 - - 4 4 -
(2)
v
0
d[v] 2
p[v] 1
1 2 3 4 5 6 7
1 1 2 0 1 1 2
4 4 2 - 4 4 5
(3)
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-56
Algorithmus für erweiterte Breitensuche
Eingabe: Startknoten s und
ungewichteter Graph G
Ausgabe: Distanzfeld
d und Vorgängerfeld p
void shortestPath (Vertex s, Graph G, int[ ] d, Vertex[ ] p) {
for ( jeden Knoten v ) {
d[v] = ∞;
p[v] = undef;
}
d[s] = 0; // Distanz für Startknoten
4
3
2
v
0 1 2 3 4 5 6 7
d[v] ∞ ∞ ∞ ∞ 0 ∞ ∞ ∞
p[v] - - - - - - - -
6
7
0
1
4
while (! q.empty() ) {
v = q.remove();
for ( jeden adjazenten Knoten w von v ) {
if (d[w] == ∞) { // w noch nicht registriert
d[w] = d[v] + 1;
p[w] = v;
q.add(w);
}
}
}
Prof. Dr. O. Bittel, HTWG Konstanz
1
5
Queue<Vertex> q;
q.add(s);
}
0
2
6
5
7
3
v
0
d[v] ∞
p[v] -
1 2 3 4 5 6 7
1 1 ∞ 0 1 1 ∞
4 4 - - 4 4 -
0
1
4
5
7
2
6
3
v
0
d[v] 2
p[v] 1
Algorithmen und Datenstrukuren – Graphenalgorithmen
1 2 3 4 5 6 7
1 1 2 0 1 1 2
4 4 2 - 4 4 5
SS 2017
3-57
Analyse der Breitensuche
!  Für jeden Knoten w kann d[w] höchstens einmal von ∞ auf einen endlichen
Wert gesetzt werden. Damit kommen in die Queue q höchstens |V| Knoten.
Die while-Schleife läuft damit höchstens |V|-mal.
!  Jede Kantenrichtung wird höchstens einmal in einer der for-Schleifen
besucht.
Verwendet man die Adjazenzlistendarstellung, ist damit der Aufwand für den
Durchlauf aller for-Schleifen gleich O(|E|).
!  Damit ergibt sich mit Adjazenzlistendarstellung insgesamt:
T = O(|V| + |E|)
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-58
Teil III: Graphenalgorithmen
! 
! 
! 
! 
! 
! 
Anwendungen
Definitionen
Datenstrukturen für Graphen
Elementare Algorithmen
Topologisches Sortieren
Kürzeste Wege
- 
- 
- 
- 
- 
- 
Problemstellung
UngewichteteGraphenunderweiterteBreitensuche
DistanzgraphenundDijkstra-Algorithmus
GewichteteDigraphenundMoore-Ford-Algorithmus
NetzpläneunderweitertetopologischeSorCerung
AllekürzesteWegeingewichtetenDigraphenmitFloyd-Algorithmus
!  Minimal aufspannende Bäume
!  Flüsse in Netzwerken
!  Zusammenhangskomponenten
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-59
Algorithmus von Dijkstra – Idee (1)
!  Single-source shortest-path: Gesucht sind alle kürzesten Wege
von einem Startknoten s zu allen anderen Knoten im einem Distanz-Graphen
(Gewichte sind positiv).
!  Ähnlich wie bei der Breitensuche werden alle Knoten, die als nächstes besucht werden
können, in einer Liste K (Kandidatenliste) gehalten.
!  Aus der Kandidatenliste wird immer der Knoten mit der kleinsten Distanz als nächster
besucht.
1
6
7
1
8
8
3
1
2
0
2
4
3
2
6
4
1
v
0 1 2 3 4 5 6 7 8
d[v] 0 1 6 2 2 ∞ ∞ ∞ ∞
p[v] - 0 0 0 0 - - - 7
1
Kandidatenliste
Bereits besucht
2
d[v] Distanz zwischen v und Startknoten s = 0
4
Prof. Dr. O. Bittel, HTWG Konstanz
Als nächstes
wird Knoten 1
mit Distanzwert 1
besucht.
5
p[v] Vorgängerknoten im kürzesten Weg nach v
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-60
Algorithmus von Dijkstra – Idee (2)
!  Wird ein neuer Knoten v besucht, dann kommen eventuell neue Nachbarknoten zur
Kandidatenliste.
!  Bei allen Nachbarknoten wird geprüft, ob ein Weg über v einen kürzeren Weg ergibt.
1
6
7
1
1
6
8
7
8
3
1
2
0
2
4
3
2
6
4
1
1
8
8
3
1
7
2
Knoten 1
1
0
2
wird besucht
5
4
5
v
0 1 2 3 4 5 6 7 8
d[v] 0 1 6 2 2 ∞ ∞ ∞ ∞
p[v] - 0 0 0 0 - - - -
4
3
2 d(2) = 5
6
4
1
7
Knoten 3
1
wird besucht
5
…
4
5
v
0 1 2 3 4 5 6 7 8
d[v] 0 1 5 2 2 6 ∞ ∞ ∞
p[v] - 0 1 0 0 1 - - -
Knoten 5 ist zur Kandidatenliste neu dazu gekommen.
Der Distanzwert von Knoten 2 hat sich von 6 auf 5 verbessert,
da der Weg über Knoten 1 kürzer ist als der bisherige.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-61
Algorithmus von Dijkstra
Eingabe: Startknoten s und
Distanzgraph G
void shortestPath (Vertex s, DistanzGraph G, int[ ] d, Vertex[ ] p ) {
(1)
(2)
(3)
(4)
}
kl = ∅; // Kandidatenliste
for (jeden Knoten v) {
d[v] = ∞;
p[v] = undef;
}
d[s] = 0; // Startknoten
kl.insert(s);
Ausgabe: Distanzfeld d
und Vorgängerfeld p
!  In der Kandidatenliste sind alle Knoten
abgespeichert, die als nächstes besucht
werden können.
!  Die Zugriffsoperationen auf die
Kandidatenliste sind rot gekennzeichnet.
while (! kl.empty() ) {
!  Mögliche Implementierungen werden auf
lösche Knoten v aus kl mit d[v] minimal;
der nächsten Folie diskutiert.
for ( jeden adjazenten Knoten w von v ) {
if (d[w] == ∞) // w noch nicht besucht und nicht in Kandidatenliste
kl.insert(w);
if (d[v] + c(v,w) < d[w]) {
Distanzwert für w verbessert sich.
p[w] = v;
Kürzester Weg nach w geht nun über v.
d[w] = d[v] + c(v,w);
}
Falls in Zeile (3) w neu in kl eingefügt
}
wurde, wird d[w] von ∞ zu einem endlichen
}
Wert verbessert.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-62
Eigenschaften
!  Die d-Werte der besuchten Knoten in Zeile (2) sind monoton steigend.
Begründung: die d-Werte der anderen Kandidaten können nicht kleiner sein
als der d-Wert von v. In Zeile (4) kann auch kein kleinerer d-Wert als
der d-Wert von v entstehen, da die Gewichte der Kanten c(v,w) positiv sind.
while (! kl.empty() ) {
Knoten v wird besucht.
(2)
lösche Knoten v aus kl mit d[v] minimal;
for ( jeden adjazenten Knoten w von v ) {
if (d[w] == ∞) // w noch nicht besucht und nicht in Kandidatenliste
(3)
kl.insert(w);
if (d[v] + c(v,w) < d[w]) {
p[w] = v;
(4)
d[w] = d[v] + c(v,w);
}
}
}
!  Daher: sobald ein Knoten v besucht wird (Zeile (2)), ist sein kürzester Weg bekannt.
!  Wird ein kürzester Weg von einem Startknoten s zu einem bestimmten Zielknoten z
gesucht, dann kann das Verfahren abgebrochen werden, sobald v = z besucht wird.
!  Ein Knoten wird höchstens einmal in die Prioritätsliste eingefügt. Daher kl.size() ≤ |V|.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-63
Analyse (1)
Kandidatenliste als Index-Heap
!  Speichere Kandidaten in einem Index-Heap (siehe Kap. 2, Prioritätslisten), wobei als
Priorität der Distanzwert aus Feld d genommen wird.
!  Ein Index-Heap bietet eine effiziente Realisierung folgender Operationen an:
-  insert(v, d) in O(log|V|):
fügt Element v mit Priorität d ein. Aufruf in (1) und (3).
-  v = delMin() in O(log|V|):
löscht Element mit kleinster Priorität. Aufruf in (2).
-  change(v, dNeu) in O(log|V|):
ändert den Vorrangwert des Elements v in dNeu. Aufruf in (4).
Analyse des Dijkstra-Algorithmus mit Kandidatenliste als Index-Heap
!  Jede Kantenrichtung wird genau einmal in einer der for-Schleifen betrachtet.
Da (3) und (4) in O(log|V|) geht, ist für alle for-Schleifen insgesamt O(|E| log|V|)
notwendig.
!  Hinzu kommt höchstens |V|-mal die Ausführung von (2), was O(|V| log|V|) bedeutet.
!  Insgesamt: T = O(|E| log|V|)
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-64
Analyse (2)
Kandidatenliste als unsortiertes Feld
!  Speichere Kandidaten in einem unsortierten Feld.
!  Damit benötigt die Operation in Zeile (2) (Minimum bestimmen) O(|V|)
und die Operationen in (1), (3) und (4) benötigen O(1).
Analyse des Dijkstra-Algorithmus mit Kandidatenliste als unsortiertes Feld
!  Die while-Schleife wird höchstens |V|-mal durchgeführt.
!  Für (2) ist O(|V|) notwendig.
!  Jede Kantenrichtung wird genau einmal in einer der for-Schleifen betrachtet.
Da (3) und (4) in O(1) geht, ist für alle for-Schleifen insgesamt O(|E|) notwendig.
!  Insgesamt: T = O(|V|2)
Fazit:
!  Ist der Graph dicht besetzt, d.h. |E| = O(|V|2),
dann ist die Variante mit Kandidatenliste als unsortiertes Feld besser.
!  Ist der Graph dünn besetzt, d.h. |E| = O(|V|),
dann ist die Variante mit Kandidatenliste als Prioritätsliste besser.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-65
Beispiel zu Dijkstra-Algorithmus (1)
1
6
7
1
1
6
8
7
1
2
0
2
4
3
2
6
4
1
8
8
8
3
1
3
1
7
2
Knoten 0
1
2
wird besucht
5
4
0
v
0 1 2 3 4 5 6 7 8
d[v] 0 ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞
p[v] - - - - - - - - -
1
6
7
2
6
4
1
7
1
5
4
4
5
3
5
1
8
v
0 1 2 3 4 5 6 7 8
d[v] 0 1 6 2 2 ∞ ∞ ∞ ∞
p[v] - 0 0 0 0 - - - -
8
Knoten 1
wird besucht
3
1
2
0
2
4
Prof. Dr. O. Bittel, HTWG Konstanz
3
6
1
2
Knoten 3
4
7
wird besucht
1
5
4
5
v
0 1 2 3 4 5 6 7 8
d[v] 0 1 5 2 2 6 ∞ ∞ ∞
p[v] - 0 1 0 0 1 - - -
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-66
Beispiel zu Dijkstra-Algorithmus (2)
1
6
7
1
1
6
8
7
2
1
0
2
4
3
2
6
4
1
8
8
8
3
1
3
Knoten 4
7
2
1
wird besucht
1
0
2
5
4
v
0 1 2 3 4 5 6 7 8
d[v] 0 1 5 2 2 6 ∞ ∞ ∞
p[v] - 0 1 0 0 1 - - -
1
6
7
1
8
2
6
4
1
7
1
5
4
4
5
3
5
v
0 1 2 3 4 5 6 7 8
d[v] 0 1 5 2 2 6 3 ∞ ∞
p[v] - 0 1 0 0 1 4 - -
8
3
Knoten 6
wird besucht
2
1
0
2
4
Prof. Dr. O. Bittel, HTWG Konstanz
3
2
6
4
1
Knoten 7
7
wird besucht
1
5
4
5
v
0 1 2 3 4 5 6 7 8
d[v] 0 1 5 2 2 6 3 4 ∞
p[v] - 0 1 0 0 1 4 6 -
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-67
Beispiel zu Dijkstra-Algorithmus (3)
1
6
7
1
8
8
3
2
1
0
2
4
3
2
6
4
1
7
1
Kürzeste Wege für Startknoten s = 0
sind nun bekannt für die Zielknoten:
1, 3, 4, 6, 7.
Kürzester Weg von 0 nach 7 ist 0, 4, 6, 7
mit Distanz d = 4.
5
4
5
v
0 1 2 3 4 5 6 7 8
d[v] 0 1 5 2 2 6 3 4 5
p[v] - 0 1 0 0 1 4 6 7
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-68
A*-Algorithmus: Verbesserung des Dijkstra-Algorithmus
!  Ist der kürzeste Weg zu genau einem Zielknoten z gesucht und lässt sich die Distanz
von einem Knoten v zum Zielknoten z durch eine Schätzfunktion h(v,z) abschätzen,
dann lässt sich der Algorithmus von Dijkstra optimieren.
!  Statt den Knoten v mit
d[v] minimal
als nächstes zu besuchen zu besuchen, wird der Knoten v besucht mit
d[v] + h(v,z) minimal.
!  Damit werden Knoten v bevorzugt, die näher zum Zielknoten liegen.
!  Sind die Knoten des Graphen Punkte in der Euklidischen Ebene und die Gewichte
gleich den Euklidischen Abständen (Luftlinienabstand), dann lässt sich als
Schätzfunktion h(v,z) der Euklidische Abstand zwischen v und z wählen.
z
…
h(1,z)
h(2,z)
…
1
…
1
s
Prof. Dr. O. Bittel, HTWG Konstanz
4
2
Wird der kürzeste Weg von s nach z
gesucht, wird zuerst Knoten 2 besucht,
weil dieser näher zu z liegt.
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-69
A*- Algorithmus
boolean shortestPath (Vertex s, Vertex z, DistanzGraph G, int[ ] d, Vertex[ ] p ) {
kl = ∅;
// Kandidatenliste
for (jeden Knoten v) {
d[v] = ∞;
p[v] = undef;
}
d[s] = 0; // Startknoten
kl.insert(s);
Ausgabe: Distanzfeld d und
Vorgängerfeld p
Eingabe: Startknoten s, Zielknoten z
und Distanzgraph G
while (! kl.empty() ) {
lösche Knoten v aus kl mit d[v] + h(v,z) minimal;
Änderungen gegenüber
if (v == z) // Zielknoten z erreicht
dem Diskstra-Algorithmus
return true;
sind blau gekennzeichnet.
for ( jeden adjazenten Knoten w von v ) {
if (d[w] == ∞) // w noch nicht besucht und nicht in Kandidatenliste
kl.insert(w);
if (d[v] + c(v,w) < d[w]) {
p[w] = v;
d[w] = d[v] + c(v,w);
}
}
}
return false;
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-70
Korrektheit des A*- Algorithmus
!  Das A*-Verfahren liefert immer einen kürzesten Weg, falls der Zielknoten z erreichbar
ist und die Schätzfunktion h folgende Eigenschaften erfüllt:
(1)  h ist zulässig:
0 ≤ h(u,v) ≤ d(u,v) für alle Knoten u, v
(h ist positiv und unterschätzt die Distanz d)
(2)  h ist monoton:
h(u,v) ≤ c(u,w) + h(w,v) für alle Knoten u, v, w
v
h(u,v)
u
c(u,w)
h(w,v)
w
!  Die Euklidische Abstandsfunktion erfüllt die beiden oberen Bedingungen.
!  Die Schätzfunktion h(u,v) = 0 erfüllt trivialerweise beide Bedingungen. Man erhält dann
genau den Algorithmus von Dijkstra.
!  In der Literatur wird die Schätzfunktion auch Heuristik genannt
(Heuristik = Strategie zur Lösungssuche)
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-71
Vergleich A*- und Dijkstra-Algorithmus
Dijkstra-Algorithmus
A*-Algorithmus
5
7
4
7
1
5
6
3
2
1
4
5
2
s
3
3
8
z
1
2
s
3
z
4
4
!  Schätzfunktion h und Kantengewicht c sind als
Euklidischer Abstand definiert.
!  Im Beispiel wird kürzester Weg von Startknoten s
nach Zielknoten z berechnet.
!  Reihenfoge der besuchten Knoten in rot.
!  Berechnete kürzeste Wege sind blau markiert.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-72
Schlechtes Verhalten von A* bei Sackgassen
!  A* geht direkt auf das Ziel z zu, läuft aber in eine
Sackgasse.
z
s
!  A* besucht nun einen Weg, der sich anfangs vom
Ziel entfernt und daher nicht früher betrachtet wurde.
s
Prof. Dr. O. Bittel, HTWG Konstanz
z
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-73
Teil III: Graphenalgorithmen
! 
! 
! 
! 
! 
! 
Anwendungen
Definitionen
Datenstrukturen für Graphen
Elementare Algorithmen
Topologisches Sortieren
Kürzeste Wege
- 
- 
- 
- 
- 
- 
Problemstellung
UngewichteteGraphenunderweiterteBreitensuche
DistanzgraphenundDijkstra-Algorithmus
GewichteteDigraphenundMoore-Ford-Algorithmus
NetzpläneunderweitertetopologischeSorCerung
AllekürzesteWegeingewichtetenDigraphenmitFloyd-Algorithmus
!  Minimal aufspannende Bäume
!  Flüsse in Netzwerken
!  Zusammenhangskomponenten
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-74
Graphen mit negativen Gewichten (1)
!  Hat ein Graph auch negative Gewichte, dann ist die Existenz eines kürzesten
Weges nicht gesichert, da Zyklen mit negativer Länge existieren können.
!  Durch mehrfaches Ablaufen solcher Zyklen kann die Weglänge beliebig klein
(negativ) werden.
1
-4
1
u
1
1
1
v
Zyklus mit Länge = 1 + 1 – 4 + 1 = -1
Damit kann die Weglänge von u nach v
beliebig klein werden.
!  Die Eindeutigkeit von einfachen Wegen ist jedoch garantiert
(d.h. Knoten dürfen nicht mehrfach auftreten).
!  Für die Bestimmung kürzester, einfacher Wege mit beliebigen Gewichten sind
bis heute keine effizienten Algorithmen bekannt.
!  Daher: Einschränkung auf beliebig gewichtete Digraphen ohne Zyklen
negativer Länge.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-75
Graphen mit negativen Gewichten (2)
!  Der Algorithmus von Dijkstra arbeitet bei Graphen mit negativ gewichteten
Kanten im allgemeinen nicht korrekt:
Mit Algorithmus von Dijkstra
berechneter Weg mit Länge = 0.8.
2
0.1
s=1
0.1
1 3
1
5
8
-10
1
z=9
-20
1
1
1
4
6
7
Kürzester Weg von s nach z
mit Länge = -16.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-76
Algorithmus von Moore und Ford - Idee
!  Single-source shortest-path:
Gegeben ist ein gewichteter Digraph (Gewichte können negativ sein).
Gesucht sind alle kürzesten Wege zwischen einem Startknoten s und allen
anderen Knoten, sofern der Graph keine negativen Zyklen enthält.
!  Ausgangspunkt ist der Algorithmus von Dijkstra.
!  Beim Algorithmus von Dijkstra wird jeder Knoten v höchstens einmal besucht
und zwar dann, wenn der kürzeste Weg zu v bekannt ist.
!  Kommen aber negative gewichtete Kanten vor, dann muss jeder Knoten im
allgemeinen mehrere Male besucht werden und zwar immer dann, wenn sich
ein noch kürzerer Weg ergibt.
!  Die Reihenfolge, in der die Knoten besucht werden, spielt nun keine Rolle
mehr. Es wird daher eine Schlange statt einer Vorrangswarteschlange
gewählt.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-77
Algorithmus von Moore und Ford
void shortestPath (Vertex s, WeightedDiGraph G, int[ ] d, Vertex[ ] p) {
Queue<Vertex> kl = ∅;
Ausgabe: Distanzfeld d und
Vorgängerfeld p
for (jeden Knoten v) {
d[v] = ∞;
p[v] = undef;
}
d[s] = 0; // Startknoten
kl.add(s);
(1)
(2)
(3)
(4)
(5)
Eingabe: Startknoten s und
gewichteter und gerichteter Graph g
while (! kl.empty()) {
v = kl.remove();
for ( jeden adjazenten Knoten w von v ) {
if (d[v] + c(v,w) < d[w]) {
p[w] = v;
d[w] = d[v] + c(v,w);
if (! kl.contains(w) )
kl.add(w);
}
}
}
}
Prof. Dr. O. Bittel, HTWG Konstanz
In der Kandidatenliste sind alle Knoten
abgespeichert, die als nächstes besucht
werden können.
Die Zugriffsoperationen auf die Kandidatenliste sind rot gekennzeichnet.
Distanzwert für w verbessert sich.
Kürzester Weg nach w geht nun über v.
w muss nochmals besucht werden.
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-78
Beispiel zu Moore- Ford-Algorithmus
2
1
s=1
2
5 3
5
1
1
1
9
-8
6
Nächster besuchter Knoten
ist rot gekennzeichnet.
7
v
1 2 3 4 5 6 7 8 9
d[v] 0 ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞
1 5 1
3
6
2
4
-4
3
3
-3
4
-5
-6
-5
Prof. Dr. O. Bittel, HTWG Konstanz
1
1
1
4
8
-10
v
1 2 3 4 5 6 7 8 9
p[v] - - - - - - - - 1 1 1
2
3
4
8
5
6
6
8
3
7
5
8
Kandiatenliste kl
1
2, 3, 4
3, 4, 8
4, 8, 5
8, 5, 6
5, 6, 9
6, 9, 8
9, 8, 3, 7
3, 7, 9
7, 9, 5
9, 5, 8
8
9
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-79
Analyse des Moore- Ford-Algorithmus
!  Enthält der Graph keine Zyklen mit negativer Länge, dann wird jeder Knoten
maximal (|V|-1)-mal in die Schlange kl eingefügt (Zeile (5) im Algorithmus).
(Begründung siehe [Turau]).
!  Wird ein Knoten |V|-mal eingefügt, dann muss es ein Zyklus mit negativer
Länge geben und es könnte im Algorithmus eine entsprechende Ausgabe
erfolgen.
!  Würde jeder Knoten genau einmal in die Schlange kl eingefügt werden, wäre
der Aufwand für alle for-Schleifen gerade O(|E|). Denn jede Kante im Graph
wird genau einmal in einer der for-Schleifen betrachtetet.
(Zeile (5) kl.contains(w) kann mit Hilfe eines Booleschen Felds in O(1)
realisiert weden).
!  Da nun aber jeder Knoten bis zu (|V|-1)-mal in die Schlange kl eingefügt
werden kann, erhält man insgesamt:
T = O(|E|*|V|).
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-80
Teil III: Graphenalgorithmen
! 
! 
! 
! 
! 
! 
Anwendungen
Definitionen
Datenstrukturen für Graphen
Elementare Algorithmen
Topologisches Sortieren
Kürzeste Wege
- 
- 
- 
- 
- 
- 
Problemstellung
UngewichteteGraphenunderweiterteBreitensuche
DistanzgraphenundDijkstra-Algorithmus
GewichteteDigraphenundMoore-Ford-Algorithmus
NetzpläneunderweitertetopologischeSorCerung
AllekürzesteWegeingewichtetenDigraphenmitFloyd-Algorithmus
!  Minimal aufspannende Bäume
!  Flüsse in Netzwerken
!  Zusammenhangskomponenten
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-81
Netzpläne
!  Netzpläne sind azyklische Digraphen mit genau einem Start und einem Endknoten.
!  Die Knoten (außer Start- und Endknoten) stellen oft gewisse Aktivitäten dar
und sind mit einem Gewicht (Aufwand wie z.B. Zeitangaben) versehen.
!  Kritischer Pfad ist ein Weg vom Start- zum Ziel-Knoten mit maximalen Aufwand.
!  Beispiel Projektplan:
-  Die Knoten sind Tätigkeiten, die in einem Projekt durchgeführt werden müssen.
-  Die Gewichte geben die Zeitdauer der Aktivitäten an.
-  Die gerichteten Kanten legen Reihenfolgenbeziehungen fest.
-  Die Länge eines kritischen Pfades gibt die frühestmögliche Projektfertigstellung an.
4
2
3
1
2
4
6
9
Start
2
0
3
6
3
1
1
5
2
Ende
11
8
2
7
10
Netzplan und kritischer Pfad mit Zeitaufwand 15
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-82
Bestimmung eines kritischen Pfades
!  Werden die an den Knoten notierten Gewichte an die einmündenden Kanten
mit umgekehrtem Vorzeichen „verschoben“, erhält man einen gewichteten,
azyklischen Digraphen.
!  Die Bestimmung des kritischen Pfades lässt sich dann zurückführen auf die
Berechnung eines kürzesten Weges vom Start- zum Zielknoten.
Start
0
-4
2
-2
-2
-3
1
-2
3
4
-3
-6
-1
5
-2
6
8
7
-1
-1
-2
-2
9
0
Ende
0
11
10
Gewichteter, azyklischer Digraph mit kürzestem Weg der Länge -15.
!  Prinzipiell ließe sich der kritische Pfad mit dem Algorithmus von Moore und Ford
bestimmen.
!  Für den Spezialfall der Netzpläne ist jedoch ein auf toplogischer Sortierung
basierender Algorithmus wesentlich effizienter.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-83
Erweiterte topologische Sortierung (1)
!  Single-source shortest-path für Netzpläne:
Gewichteter, azyklischer Digraph (Gewichte können negativ sein) mit genau
einem Startknoten s. Gesucht sind alle kürzeste Wege zwischen s und allen
anderen Knoten.
!  Besuche die Knoten wie bei der topologischen Sortierung.
Dazu werden in der Schlange alle noch nicht besuchten Knoten gehalten,
deren Vorgänger bereits besucht worden sind.
!  Bei jedem Knotenbesuch werden dann wie beim Algorithmus von Moore und
Ford für alle Nachfolgerknoten die Distanzen und der gefundene kürzeste
Weg gegebenenfalls aktualisiert.
!  Analyse (wie bei der topologischen Sortierung):
T = O(|E|+|V|).
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-84
Erweiterte topologische Sortierung (2)
Ausgabe:
Distanzfeld d und Vorgängerfeld p
void allShortestPath (WeightedDiGraph g, int[ ] d, Vertex[ ] p) {
int[ ] inDegree; // Anz. noch nicht besuchten Vorgänger
Queue<Vertex> q;
Eingabe:
Gewichteter Digraph G mit genau
einem Startknoten.
for (jeden Knoten v) {
d[v] = ∞;
p[v] = undef;
inDegree[v] = Anzahl der Vorgänger;
if (inDegree[v] == 0) {
q.add(v);
d[v] = 0;
}
}
int k = 0;
while (! q.empty() ) {
v = q.remove(); k++;
for ( jeden Nachfolger w von v ) {
if (d[v] + c(v,w) < d[w]) {
p[w] = v;
d[w] = d[v] + c(v,w);
}
if (--inDegree[w] == 0)
q.add(w);
}
}
if (q.size() > 1) {
println(“Fehler; mehr als ein Startknoten”);
return;
}
Programmteile, um die die topologische
Sortierung erweitert wurde, sind blau
gekennzeichnet.
Prof. Dr. O. Bittel, HTWG Konstanz
if (k != n)
println( “Fehler; Graph ist zyklisch”);
}
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-85
Beispiel zu erweiterten topologischen Sortierung
Start
0
-4
2
-2
-2
-3
-2
3
0 1 2 3 4 5 6 7 8
0 ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞
-3 -4
-5
-6
-8 -7
-11
-9 -13
5
9
∞
10 11
∞ ∞
-12
-11
-14 -15
-14
-15
-3
-6
-1
1
v
d[v]
4
-2
6
8
7
-1
-1
-2
-2
9
0
0
Ende
11
10
v
0 1 2 3 4 5 6 7 8 9 10 11
inDegree 0 1 1 2 1 1 1 1 1 2 2 2
0 0
1
0
0 0
0
0 0
1
1
0 0
1
0
Kand.Liste q
0
1, 2
2
3
4, 5
5, 6
6, 7, 8
7, 8
8
9, 10
10
11
!  Nächster besuchter Knoten ist rot gekennzeichnet.
!  p-Feld wurde weggelassen
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-86
Teil III: Graphenalgorithmen
! 
! 
! 
! 
! 
! 
Anwendungen
Definitionen
Datenstrukturen für Graphen
Elementare Algorithmen
Topologisches Sortieren
Kürzeste Wege
- 
- 
- 
- 
- 
- 
Problemstellung
UngewichteteGraphenunderweiterteBreitensuche
DistanzgraphenundDijkstra-Algorithmus
GewichteteDigraphenundMoore-Ford-Algorithmus
NetzpläneunderweitertetopologischeSorCerung
AllekürzesteWegeingewichtetenDigraphenmitFloyd-Algorithmus
!  Minimal aufspannende Bäume
!  Flüsse in Netzwerken
!  Zusammenhangskomponenten
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-87
Idee des Floyd-Algorithmus
!  All pairs shortest-path:
Gegegeben ist ein gewichteter Digraph (Gewichte können negativ sein).
Gesucht sind kürzeste Wege zwischen allen Knotenpaaren, sofern der Graph
keine negativen Zyklen enthält.
!  Der Algorithmus basiert im wesentlichen auf dem Optimalitätsprinzip von
Bellman, das für Graphen ohne Zyklen negativer Länge gilt.
Für jeden Knoten u auf einem kürzesten Weg von v nach w gilt:
d(v,w) = d(v,u) + d(u,w).
D.h. Teilwege von kürzesten Wege müssen
ebenfalls kürzeste Wege sein.
v
u
w
!  Der Floyd-Algorithmus arbeitet nach dem Prinzip des dynamischen
Programmierens.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-88
Einschub - dynamisches Programmieren (1)
!  Problem:
Bei rekursiven Funktionen kann es zu mehrfacher Berechnung des gleichen
Funktionswerts kommen.
!  Beispiel: Fibonacci-Funktion
int fib(int n) {
if (n == 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n-1) + fib(n-2);
}
Aufrufstruktur für fib(4):
fib(4)
fib(3)
fib(2)
fib(1)
fib(2)
fib(1) fib(1)
fib(0)
fib(0)
!  Bei Aufruf von fib(4) wird 2-mal fib(2), 3-mal fib(1) und 2-mal fib(0) berechnet.
!  Bei Aufruf von fib(24) wird 2-mal fib(22), 3-mal fib(21), 5-mal fib(20),
8-mal fib(19), 13-mal fib(18) etc. berechnet.
!  Es lässt sich zeigen, dass bei Aufruf von fib(n) insgesamt etwa (2/√5)*φn+1 – 1
Aufrufe stattfinden, wobei φ = (1+√5)/2 ≈ 1.62 (φ wird auch „Goldener Schnitt“
genannt.)
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-89
Einschub - dynamisches Programmieren (2)
Dynamisches Programmieren:
! 
Ausgangspunkt: Rekursiv definierte Funktion.
! 
Berechne rekursive Funktion mit Iteration und Tabelle statt Rekursion:
! 
Beginne mit den Basisfällen und wende dann Rekursionsschritte „aufwärts“ an, wobei
Zwischenergebnisse in einer Tabelle zur späteren Verwendung abgespeichert werden.
! 
Dynamisches Programmieren geht auf ein Verfahren zur Optimierung technischer
Prozesse von Bellman 1957 zurück.
Beispiel: Fibonacci-Funktion
int[ ] fib = new int[N+1];
Tabelle
fib[0] = 0;
fib[1] = 1;
Basisfälle
for (int n = 2; n <= N; n++)
fib[n] = fib[n-1] + fib[n-2];
Rekursionsschritt
aufwärts angewendet
Analyse der Fibonacci-Funktion:
!  dynamisch programmiertes fib: O(n)
!  rekursives fib: O(1.6n)
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-90
Einschub - dynamisches Programmieren (3)
Weiteres Beispiel aus der Kombinatorik:
!  Eine Auswahl (Teilmenge) von k verschiedenen Elementen aus einer Menge von n
Elementen heißt Kombination.
!  Die Anzahl der Kombinationen schreibt man in der Mathematik auch als:
Dieser Ausdruck ist nur für n ≥ k ≥ 0 definiert.
!  Beispiel: die Anzahl der möglichen Lottokombinationen ist gleich
.
Rekursive Formulierung
Rekursionsfall:
Um eine k-elementige Teilmenge aus einer Menge mit n
Elementen auszuwählen, kann entweder das n-te
Element weggelassen oder dazu genommen werden.
Basisfälle:
Bei k = 0 gibt es nur eine Teilmenge, nämlich die leere
Teilmenge. Bei k = n ist die Menge selbst die einzige
Teilmenge.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-91
Einschub - dynamisches Programmieren (4)
Iterative Berechnung mit Tabelle
int[ ][ ] komb= new int[N+1][N+1];
for (int n = 0; n <= N; n++) {
komb[n][0] = 1;
// Basisfall (1)
komb[n][n] = 1;
// Basisfall (2)
}
Tabelle zum Speichern der
Zwischenergebnisse:
komb[n][k] =
// Rekursionsschritt:
for (int n = 2; n <= N; n++)
for (int k = 1; k < n; k++)
komb[n][k] = komb[n-1][k] + komb[n-1][k-1];
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-92
Einschub - dynamisches Programmieren (5)
!  Beispiel: für N = 4 ergibt sich die Tabelle komb wie folgt:
n\k
0
1
0
1
1
1
1
2
1
2
1
3
1
3
3
1
4
1
4
6
4
+
2
3
4
Basisfälle
Rekursionsfall
1
komb[n][k] = komb[n-1][k] + komb[n-1][k-1];
Pascalsches Dreieck!
!  Bemerkung: Für die Berechnung von komb[n][k] würde es genügen nur
die jeweils letzte Zeile der Matrix zu speichern. Daher eindimensionales
Feld statt Matrix.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-93
Kürzeste Wege mit dynamischer Programmierung (1)
!  Nochmals die Problemstellung
-  Berechne kürzeste Wege zwischen allen Knotenpaaren in einem gewichteten,
gerichteten Graphen. Graph darf keine Zyklen negativer Länge enthalten.
-  Knoten sind durchnummeriert: v0, v1, …, vn-1.
!  Formulierung des Problems als Funktion
-  Dk(i, j) = Distanz (Länge eines kürzesten Weges) von vi nach vj, wobei nur Wege
von vi nach vj betrachtet werden, die über Knoten aus {v0, v1, …, vk} gehen.
-  Dn-1 löst unser Problem!
!  Rekursive Formulierung
-  Basisfall k = -1 (nur die direkte Verbindung von vi nach vj ist erlaubt):
D-1(i, j) = c(vi, vj) (Gewicht der Kante (vi, vj))
-  Rekursionfall (k ≥ 0):
Dk(i, j) = min { Dk-1(i, j) , Dk-1(i, k) + Dk-1(k, j) }
Länge eines kürzesten Wegs von vi
nach vj, ohne über vk zu gehen.
Länge eines kürzesten Wegs von vi nach vj über
vk setzt sich zusammen aus kürzestem Weg von
vi nach vk und kürzestem Weg von vk nach vj.
(Optimalitätsprinzip von Bellman)
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-94
Kürzeste Wege mit dynamischer Programmierung (2)
!  Iterative Berechnung von Dk mit Tabelle:
-  Dk(i, j) lässt sich für jedes k durch eine 2-dimensionale Matrix D[i][j] darstellen.
D-1 wird gemäß Basisfall initialisiert;
D0 ergibt sich aus D-1 nach dem Rekursionsfall, D1 aus D0, usw.
Dn-1 ist die Lösung unseres Problems.
-  Es genügt, Dk nur für den jeweils aktuellen Wert von k abzuspeichern.
Dk(i, j) = min { Dk-1(i, j) , Dk-1(i, k) + Dk-1(k, j) }
= min { Dk-1(i, j) , Dk(i, k) + Dk(k, j) } (denn Dk(i, k) = Dk-1(i, k) und Dk(k, j) = Dk-1(k, j))
// Tabelle
int[ ][ ] D = new int[n][n];
// Starte mit D-1:
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++)
if (i == j) D[i][j] = 0; else D[i][j] = c(vi, vj);
}
for (int k = 0; k < n; k++)
// Berechne Dk:
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (D[i][j] > D[i][k] + D[k][j])
D[i][j] = min {D[i][j] , D[i][k] + D[k][j] };
D[i][j] = D[i][k] + D[k][j]);
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-95
Speicherung der kürzesten Wege
!  Zur Speicherung der gefundenen Wege wird eine
Vorgängermatrix P eingesetzt:
P[i][j] = Vorgängerknoten von vj im kürzesten Weg von vi nach vj.
!  Damit kann der kürzeste Weg von Start s nach Ziel z
mit z beginnend in eine Liste von hinten nach vorne eingetragen werden:
List<Integer> getShortestPath (int s, int z, Vertex[ ][ ] P)
LinkedList<Integer> shortestPath;
Vertex v = z; // Ziel
while ( v != s ) {
shortestPath.addFirst(v);
v = P[s][v];
}
shortestPath.addFirst(s);
kürzester Weg
von s nach z.
return shortestPath;
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-96
Algorithmus von Floyd
void computeAllShortestPath (WightedDiGraph G, int[ ][ ] D, Vertex[ ][ ] P) {
// Initialisiere D und P:
for (int i = 0; i < n; i++) {
Ausgabe:
for (int j = 0; j < n; j++) {
Distanzmatrix D und Vorgängermatrix P
P[i][j] = undef;
if (i == j)
Eingabe:
D[i][j] = 0
Gewichteter Digraph G mit n Knoten.
else {
D[i][j] = c(vi, vj); // ∞, falls keine Kante (vi,vj) existiert
if (D[i][j] != ∞)
P[i][j] = i;
}
}
}
}
for (int k = 0; k < n; k++)
// Berechne Dk:
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (D[i][j] > D[i][k] + D[k][j]) {
D[i][j] = D[i][k] + D[k][j];
P[i][j] = P[k][j];
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-97
Analyse des Floyd-Algorithmus
!  Enthält der Graph Zyklen mit negativer Länge,
dann wird ein Diagonalelement von D negativ.
Der Algorithmus kann leicht um eine passende Abprüfung
mit entsprechender Ausgabe erweitert werden.
!  Analyse: die dreifach geschachtelten for-Schleifen ergeben:
T = O(|V|3).
!  Vergleich zu Dijkstra-Algorithmus:
!  Ist der Graph dünn-besetzt (d.h. |E| = O(|V|) und alle Gewichte positiv,
dann kann der Dijkstra-Algorithmus mit einem Index-Heap auch
für jeden Knoten aus V als Startknoten durchgeführt werden.
Damit: T = O(|V||E|log(|V|) = O(|V|2log(|V|).
Also: Dijkstra-Algorithmus besser als Floyd-Algorithmus.
!  Ist der Graph dicht besetzt und alle Gewichte positiv,
dann würde das |V|-malige Durchführen des Dijkstra-Algorithmus
(mit unsortierter Prioritätsliste) ebenfalls zu T = O(|V|3) führen.
Dann ist aber aufgrund der einfacheren Datenstruktur
der Floyd-Algorithmus besser als der Dijkstra-Algorithmus.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-98
Beispiel zu Floyd-Algorithmus
v0
v1
5
4
-2
1
-1
1
v2
-2
v4
0 5 ∞
∞ 0 -1
∞ 1 0
∞ ∞ -2
1 ∞ ∞
4
∞
∞
0
∞
∞
-2
∞
-1
0
0 5 ∞
∞ 0 -1
∞ 1 0
∞ ∞ -2
1 6 ∞
D-1
4
0
2
-
1
3
-
P-1
4
∞
∞
0
5
∞
-2
∞
-1
0
0 5 4 4 3
∞ 0 -1 ∞ -2
∞ 1 0 ∞ -1
∞ ∞ -2 0 -1
1 6 5 5 0
D0
0
-
1
3
-
4
0
2
0
1
3
-
P0
Prof. Dr. O. Bittel, HTWG Konstanz
v3
-1
0 5 4
∞ 0 -1
∞ 1 0
∞ -1 -2
1 6 5
D1
0
0
1
3
-
4
0
2
0
1
1
3
1
P1
4 3
∞ -2
∞ -1
0 -3
5 0
0 3 2
∞ 0 -1
∞ 1 0
∞ -1 -2
1 4 3
D2
0
0
1
1
1
3
-
4
0
2
2
0
1
1
3
1
P2
4 1
∞ -2
∞ -1
0 -3
5 0
0
-1
0
-2
1
3
0
1
-1
4
D3
0
0
1
1
1
1
-
4
2
2
2
2
3
1
3
3
2
-1
0
-2
3
4 1
3 -2
4 -1
0 -3
5 0
D4
0
0
P3
Algorithmen und Datenstrukuren – Graphenalgorithmen
1
1
1
1
-
4
4
4
4
2
2
2
2
3
1
3
3
0
0
0
0
1
1
1
1
-
P4
SS 2017
3-99
Teil III: Graphenalgorithmen
! 
! 
! 
! 
! 
! 
! 
Anwendungen
Definitionen
Datenstrukturen für Graphen
Elementare Algorithmen
Topologisches Sortieren
Kürzeste Wege
Minimal aufspannende Bäume
- 
- 
- 
- 
Problemstellung
AlgorithmusvonPrim
AlgorithmusvonKruskal
Union-Find-Strukturen
!  Flüsse in Netzwerken
!  Zusammenhangskomponenten
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-100
Problemstellung (1)
Definition
Sei G = (V,E) ein ungerichteter, gewichteter Graph.
Dann ist B ein minimal aufspannender Baum, falls B folgende Eigenschaften erfüllt:
(1)  B = (V, E‘) mit E‘ ⊆ E;
d.h. B ist ein Teilgraph von G mit gleicher Knotenmenge
(2)  B ist ein Baum;
d.h. ein azyklischer, zusammenhängender Graph
(3)  Die Summe der Kantengewichte von B ist minimal;
d.h. es gibt keinen anderen Baum, der Eigenschaften (1) und (2) erfüllt und eine
kleinere Kantengewichtssumme hat.
Beispiel
7
3
2
3
1
5
6
4
1
4
1
2
2
Ungerichteter Graph G
Prof. Dr. O. Bittel, HTWG Konstanz
2
1
Minimal aufspannender Baum von G
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-101
Problemstellung (2)
Bemerkung
!  Ein azyklischer (d.h. kreisloser) zusammenhängender Graph ist ein Baum:
man nehme einfach einen beliebigen Knoten als Wurzel und „verschiebe die anderen
Knoten nach unten“.
1
2
1
3
6
4
5
6
4
3
2
5
!  Ein Baum garantiert, dass es zwischen je zwei Knoten immer
genau einen Weg gibt. Er spannt damit die Knotenmenge auf.
!  Der minimal aufspannende Baum muss nicht eindeutig sein.
Typische Anwendung
!  Zwischen n Orten soll ein Versorgungsnetz (Kommunikation, Strom, Wasser, etc.)
aufgebaut werden, so dass je 2 Orte direkt oder indirekt miteinander verbunden sind.
Die Verbindungskosten zwischen 2 Orte seien bekannt.
!  Gesucht ist ein Versorgungsnetz mit den geringsten Kosten.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-102
Teil III: Graphenalgorithmen
! 
! 
! 
! 
! 
! 
! 
Anwendungen
Definitionen
Datenstrukturen für Graphen
Elementare Algorithmen
Topologisches Sortieren
Kürzeste Wege
Minimal aufspannende Bäume
- 
- 
- 
- 
Problemstellung
AlgorithmusvonPrim
AlgorithmusvonKruskal
Union-Find-Strukturen
!  Flüsse in Netzwerken
!  Zusammenhangskomponenten
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-103
Idee des Algorithmus von Prim
!  Der Algorithmus von Prim ist eine leichte Modifikation
des Algorithmus von Dijkstra.
!  Zu jedem Zeitpunkt bilden die bereits besuchten Knoten einen
minimal aufspannenden Baum.
Anfangs ist noch kein Knoten besucht.
Der minimal aufspannende Baum wird wie bei den Algorithmen
für kürzeste Wege in einem Vorgängerfeld gehalten.
!  Alle Knoten, die als nächstes besucht werden können, werden in einer
Kandidatenliste gehalten.
Zu Anfang wird mit einem beliebigen Knoten als Kandidat begonnen.
!  Von den Kandidaten wird derjenige Knoten als nächster besucht,
der über die billigste Kante zu erreichen ist.
Beachte den Unterschied zum Dijkstra-Algorithmus:
Beim Algorithmus von Djikstra wird immer der Knoten
mit der kürzesten Distanz zum Startknoten s besucht.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-104
Beispiel zu Algorithmus von Prim (1)
7
2
3
3
Zu Beginn ist Knoten 1 einziger Kandidat.
3
2
1
1
6
5
5
6
1
Kandidatenknoten sind rot unterlegt.
4
4
2
Knoten 1 wird besucht
7
2
3
Alle noch nicht besuchten Nachbarn von Knoten 1
werden Kandidaten.
1
Bereits besuchte Knoten und ausgewählte Kanten
sind blau unterlegt und bilden den bisher gefundenen
minimal aufspannenden Baum.
3
3
2
1
1
6
5
5
6
2
Prof. Dr. O. Bittel, HTWG Konstanz
4
4
Knoten v
Vorgängerfeld p[v]
1
-
2
-
3
-
4
-
5
-
Algorithmen und Datenstrukuren – Graphenalgorithmen
6
SS 2017
3-105
Beispiel zu Algorithmus von Prim (2)
Knoten 6 wird besucht, da Knoten 6
über die günstigste Kante zu erreichen ist.
7
2
3
3
3
2
1
1
6
5
5
6
1
4
Knoten v
Vorgängerfeld p[v]
1
-
2
-
3
-
4
-
5
-
6
1
5
-
6
1
4
2
Knoten 2 wird besucht.
(Alternativ hätte man auch Knoten 4
besuchen können.)
7
2
3
3
3
2
1
1
5
5
6
Prof. Dr. O. Bittel, HTWG Konstanz
6
2
1
Knoten v
Vorgängerfeld p[v]
1
-
2
6
3
-
4
-
4
4
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-106
Beispiel zu Algorithmus von Prim (3)
Knoten 4 wird besucht.
7
2
3
3
3
2
1
1
6
5
5
6
1
4
Knoten v
Vorgängerfeld p[v]
1
-
2
6
3
4
4
6
5
4
6
1
2
6
3
4
4
6
5
-
6
1
4
2
Knoten 3 wird besucht.
7
2
3
3
3
2
1
1
5
5
6
Prof. Dr. O. Bittel, HTWG Konstanz
6
2
1
4
Knoten v
Vorgängerfeld p[v]
1
-
4
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-107
Beispiel zu Algorithmus von Prim (4)
Knoten 5 wird besucht.
7
2
3
3
3
2
1
1
6
5
5
6
2
1
4
Knoten v
Vorgängerfeld p[v]
1
-
2
6
3
4
4
6
5
4
6
1
4
Fertig, da alle Knoten besucht!
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-108
Algorithmus von Prim
void minimumSpanningTree(Graph G, Vertex[ ] p ) {
kl = ∅; // Kandidatenliste
boolean[ ] istImBaum; // Knoten ist im aufspannenden Baum
for (jeden Knoten v) {
Eingabe: Gewichteter Graph G.
c[v] = ∞; p[v] = undef; istImBaum[v] = false;
}
Ausgabe: Vorgängerfeld p, das den minimal
s = 1; c[s] = 0; // irgendeinen Startknoten wählen
aufspannenden Baum darstellt.
kl.insert(s);
while (! kl.empty() ) {
In der Kandidatenliste kl sind alle Knoten
lösche Knoten v aus kl mit c[v] minimal;
abgespeichert, die als nächstes besucht
istImBaum[v] = true;
werden können.
for (jeden Nachbarknoten w von v)
if ( ! istImBaum[w]) {
if (c[w] == ∞) // w noch nicht in Kandidatenliste
kl.insert(w);
if ( c(v,w) < c[w] ) { // c[w] verbessert sich c[v] gibt für jeden Kandidat das Gewicht der
billigsten Kante an, mit der er von den bereits
p[w] = v;
besuchten Knoten erreicht werden kann. Für
c[w] = c(v,w);
die noch nicht besuchten Knoten, die noch
}
keine Kandidaten sind, ist c[w] = ∞.
}
}
}
if (istImBaum ist nicht true für alle Knoten)
println("es existiert kein aufspannder Baum);
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-109
Analyse des Algorithmus von Prim
!  Mit der gleichen Begründung wie beim Algorithmus von Dijkstra gilt:
(1)  Kandidatenliste als einfaches (unsortiertes) Feld
T = O(|V|2)
(2)  Kandidatenliste als Prioritätsliste
T = O(|E| log|V|)
Fazit:
!  Ist der Graph dicht besetzt, d.h. |E| = O(|V|2 ),
dann ist die Variante (1) besser.
!  Ist der Graph dünn besetzt, d.h. |E| = O(|V|),
dann ist die Variante (2) besser.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-110
Teil III: Graphenalgorithmen
! 
! 
! 
! 
! 
! 
! 
Anwendungen
Definitionen
Datenstrukturen für Graphen
Elementare Algorithmen
Topologisches Sortieren
Kürzeste Wege
Minimal aufspannende Bäume
- 
- 
- 
- 
Problemstellung
AlgorithmusvonPrim
AlgorithmusvonKruskal
Union-Find-Strukturen
!  Flüsse in Netzwerken
!  Zusammenhangskomponenten
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-111
Idee des Algorithmus von Kruskal
!  Es wird ein Wald von minimal aufspannenden Bäumen verwaltet.
(Wald = Menge von Bäumen)
!  Start:
Zu Anfang besteht der Wald aus allen Bäumen mit jeweils genau einem
Knoten.
!  Vereinigungsschritt:
-  Suche die billigste Kante k, die zwei unterschiedliche Bäume
aus dem Wald verbindet.
Vereinige die 2 Bäume mit der Kante k zu einem größeren Baum.
-  Es werden solange Vereinigungsschritte durchgeführt, bis nur noch ein
Baum übrig bleibt. Das ist dann der minimal aufspannende Baum.
!  Anmerkung:
Der Wald von Bäumen lässt sich besonders effizient mit einer so genannten
Union-Find-Struktur darstellen (siehe nächster Abschnitt).
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-112
Beispiel zu Algorithmus von Kruskal (1)
7
2
3
3
3
2
1
1
6
5
5
6
Am Anfang besteht der
Wald aus 6 Bäumen
mit jeweils genau einem Knoten.
1
4
4
2
Kante k = (1,6) mit Gewicht 1 wird gewählt
7
2
3
3
3
2
1
1
6
5
5
6
2
Prof. Dr. O. Bittel, HTWG Konstanz
1
Die beiden Bäumen, die aus dem
Knoten 1 bzw. 6 bestehen, werden zu
einem Baum vereinigt.
Der Wald besteht damit nun aus
5 Bäumen.
4
4
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-113
Beispiel zu Algorithmus von Kruskal (2)
Kante k = (3,4) mit Gewicht 1 wird gewählt
7
2
3
3
3
2
1
1
6
5
5
6
1
Die beiden Bäumen, die aus dem
Knoten 3 bzw. 4 bestehen, werden zu
einem Baum vereinigt.
Der Wald besteht damit nun aus
4 Bäumen.
4
4
2
Kante k = (2,6) mit Gewicht 2 wird gewählt
7
2
3
3
3
2
1
1
5
5
6
Prof. Dr. O. Bittel, HTWG Konstanz
6
2
1
Der Baum mit den Knoten {1, 6} und
der Baum mit dem Knoten 2 werden
zu einem Baum vereinigt.
Der Wald besteht damit nun aus
3 Bäumen.
4
4
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-114
Beispiel zu Algorithmus von Kruskal (3)
Kante k = (4,6) mit Gewicht 2 wird gewählt
7
2
3
3
3
2
1
1
6
5
5
6
1
Der Baum mit den Knoten {1,2,6} und
der Baum mit den Knoten {3,4}
werden zu einem Baum vereinigt.
Der Wald besteht damit nun aus
2 Bäumen.
4
4
2
Kante k = (4,5) mit Gewicht 4 wird gewählt
7
2
3
3
3
2
1
1
5
5
6
Prof. Dr. O. Bittel, HTWG Konstanz
6
2
1
Der letzte Vereinigungsschritt führt zu
einem Wald mit genau einem Baum.
⇒ Minimal aufspannender Baum.
4
4
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-115
Algorithmus von Kruskal
Rückgabe: Minimal aufspannender Baum
Eingabe: Graph G = (V,E).
List<Edge> minimumSpanningTree(Graph G) {
(1)
(2)
(3)
(4)
(5)
(6)
UnionFind forest = { {v} / v ∈ V};
PriorityQueue<Edge> edges(E);
List<Edge> minSpanTree = ∅;
while ( forest.size() != 1 && ! edges.isEmpty() ) {
(v,w) = edges.delMin();
t1 = forest.find(v);
t2 = forest.find(w);
if (t1 != t2) {
forest.union(t1,t2);
minSpanTree.add(v,w);
}
}
if (edges.isEmpty() && forest.size() != 1)
return “es existiert kein aufspannender Baum”;
else
return minSpanTree;
}
Prof. Dr. O. Bittel, HTWG Konstanz
forest ist eine Menge von Bäumen, die
anfangs aus jeweils genau einem Knoten
aus V bestehen. Der Datentyp UnionFind
wird im nächsten Abschnitt erklärt.
Alle Kanten werden mit ihren Gewichten in
einer Prioritätsliste gespeichert, so dass
effizient auf die Kante mit dem kleinsten
Gewicht zugegriffen werden kann.
Solange der Wald noch mehr als ein
Baum enthält und es noch Kanten gibt.
Wähle Kante mit kleinstem Gewicht,
die 2 Bäume aus dem Wald verbindet.
forest.find(v) liefert denjenigen Baum
zurück, in dem v enthalten ist.
Vereinige die beiden Bäume zu einem
Baum.
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-116
Analyse des Algorithmus von Kruskal
!  Mit dem Datentyp UnionFind lässt sich eine Menge (hier: Wald) von disjunkten
Mengen (hier Bäume) effizient verwalten mit den Operationen find und union
(siehe nächster Abschnitt).
Damit:
-  find in (3) und (4): O(log|V|)
-  union in (5): O(1)
•  Eine Prioritätsliste PriorityQueue läßt sich mit einer Heap-Struktur implementieren.
Damit:
-  Aufbau einer PriorityQueue in Zeile (1): O(|E|)
-  delMin in Zeile (2): O(log|E|)
!  Der minimal aufpannende Baum minSpanTree ist als
einfache Kanten-Liste realisiert. Damit ist (6) in O(1) durchführbar.
!  Insgesamt:
Im schlechtesten Fall wird delMin (Zeile (2)) gefolgt von Zeile (3), (4) und
evtl. (5) und (6) |E|-mal durchgeführt.
T = O(|E|(log|E| + log |V|))
Also:
T = O(|E| log|V|).
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-117
Teil III: Graphenalgorithmen
! 
! 
! 
! 
! 
! 
! 
Anwendungen
Definitionen
Datenstrukturen für Graphen
Elementare Algorithmen
Topologisches Sortieren
Kürzeste Wege
Minimal aufspannende Bäume
- 
- 
- 
- 
Problemstellung
AlgorithmusvonPrim
AlgorithmusvonKruskal
Union-Find-Strukturen
!  Flüsse in Netzwerken
!  Zusammenhangskomponenten
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-118
Problemstellung
!  Effiziente Verwaltung einer Partitionierung (disjunkte Zerlegung in Teilmengen)
einer Grundmenge G = {0, 1, 2, 3, ...,n-1}:
G = S0 ∪ S1 ∪ ... ∪ Sm-1,
5
4
0
3
1
2
9
7
Si ∩ Sj = ∅ für i ≠ j
G = {0, 1, 2, 3, ..., 9}
8
= {3, 4, 5} ∪ {0} ∪ {1, 9} ∪ {2, 6, 7} ∪ {8}
6
{2, 7, 6}
!  folgende Operationen sollen unterstützt werden:
-  S = find(e):
liefert diejenige Menge S zurück, in der das Element e enthalten ist.
-  union(S1,S2) :
vereinigt die beiden Mengen S1 und S2.
!  Problem: Wie sollen Mengen benannt werden.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-119
Idee (1)
!  Als Name einer Menge S wird irgendein festes
Element (Repräsentant) gewählt.
5
4
0
3
1
2
9
7
G = {0, 1, 2, 3, ..., 9}
8
= {3, 4, 5} ∪ {0} ∪ {1, 9} ∪ {2, 6, 7} ∪ {8}
=5∪0∪9∪6∪8
6
{2, 7, 6} = 6
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-120
Idee (2)
5
4
0
1
2
9
7
3
8
find(7) liefert 6 zurück.
6
union(5, 9) ergibt folgende
neue Partitionierung.
5
4
3
1
9
Prof. Dr. O. Bittel, HTWG Konstanz
2
0
7
8
6
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-121
Datenstruktur für Union-Find
!  Verwalte Mengen als Bäume, wobei Bäume mit einem Vorgänger- oder besser
Elternfeld p dargestellt werden.
!  Der Name (Repräsentant) einer Menge ist die Wurzel.
Beispiel
!  Partitionierung:
G = {0, 1, 2} ∪ {4, 6, 7, 9, 10, 11} ∪ {3} ∪ {5, 8}
!  Bäume:
6
0
2
3
4
1
11
7
10
Namen
(Repräsentant) der
jeweiligen Menge
5
8
9
!  Elternfeld:
e
0
1
p[e] -1 0
2
3
4
5
6
7
8
9 10 11
0 -1 6 -1 -1 4
5
4
6
6
p[e] gibt den Elternknoten
(Vorgängerknoten) zu p an.
Falls e eine Wurzel ist, dann ist
p[e] = -1 (d.h. kein Elternknoten)
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-122
Find-Algorithmus
int find(int e) {
if (p[e] == -1) // e ist Wurzel
return e;
else
return find(p[e]);
}
Beispiel
!  Partitionierung:
G = {0, 1, 2} ∪ {4, 6, 7, 9, 10, 11} ∪ {3} ∪ {5, 8}
!  Bäume:
6
0
2
4
1
3
11
7
5
12
8
find(9)
liefert 6 zurück.
9
!  Elternfeld:
e
0
1
p[e] -1 0
2
7
8
9 10 11
0 -1 6 -1 -1 4
5
4
Prof. Dr. O. Bittel, HTWG Konstanz
3
4
5
6
6
6
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-123
Union-Algorithmus - Idee
!  Werden zwei Mengen vereinigt, dann wird der kleinere Baum als Kind der Wurzel
des größeren Baums eingehängt.
!  Beispiel:
0
2
4
1
11
7
5
3
6
10
e
8
0 1 2 3 4 5 6 7 8 9 10 11
p[e] -1 0 0 -1 6 -1 -1 4 5 4 6 6
9
union(0, 6) vereinigt die Menge {0, 1, 2} und die Menge {4, 6, 7, 9, 10, 11}:
6
0
2
4
1 7
Prof. Dr. O. Bittel, HTWG Konstanz
3
11
10
5
8
e
0 1 2 3 4 5 6 7 8 9 10 11
p[e] 6 0 0 -1 6 -1 -1 4 5 4 6 6
9
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-124
Union-By-Height
!  Es wird der Baum mit kleinerer Höhe an die Wurzel des Baums
mit größerer Höhe gehängt.
!  Die Höhe h für ein Baum lässt sich bei p[e], wobei e die Wurzel ist,
als negative Zahl abspeichern: p[e] = -1-h
void unionByHeight (int s1, int s2) {
s1 und s2 müssen Repräsentanten
einer Menge sein.
if (p[s1] >= 0 || p[s2] >= 0)
return;
if (s1 == s2)
return;
Falls s1 und s2 dieselbe Menge ist,
dann mache nichts.
if ( -p[s1] < -p[s2] ) // Höhe von s1 < Höhe von s2
p[s1] = s2;
else {
if ( -p[s1] == -p[s2] )
p[s1]--; // Höhe von s1 erhöht sich um 1
p[s2] = s1;
}
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-125
Union-By-Size
!  Es wird der Baum mit geringerer Anzahl Knoten an die Wurzel des Baums mit
größerer Anzahl Knoten gehängt.
!  Die Anzahl der Knoten n für ein Baum lässt sich bei p[e], wobei e die Wurzel ist, als
negative Zahl abspeichern: p[e] = -1-n
void unionBySize (int s1, int s2) {
if (p[s1] >= 0 || p[s2] >= 0)
return;
if (s1 == s2)
return;
if (-p[s1] < -p[s2] ) { // Größe von s1 < Größe von s2
p[s2] = p[s2] - p[s1] + 1; // Größe von s2 wächst um Größe von s1
p[s1] = s2;
}
else {
p[s1] = p[s1] - p[s2] + 1; // Größe von s1 wächst um Größe von s2
p[s2] = s1;
}
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-126
Beispiel für unionByHeight (1)
2
4
1
11
7
5
3
6
0
10
e
8
p[e] -2 0 0 -1 6 -2 -4 4 5 4 6
13
9
0 1 2 3 4 5 6 7 8 9 10 11 12 13
6
7 11
Wurzeln
12
Höheninformation
unionByHeight(3, 5)
6
0
2
4
1
7
5
11
9
10
3
e
8
0 1 2 3 4 5 6 7 8 9 10 11 12 13
p[e] -2 0 0 5 6 -2 -4 4 5 4 6
6
7 11
13
12
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-127
Beispiel für unionByHeight (2)
6
0
2
5
4
1
11
7
10
e
3
8
0 1 2 3 4 5 6 7 8 9 10 11 12 13
p[e] -2 0 0 5 6 -2 -4 4 5 4 6
6
7 11
13
9
12
unionByHeight(0, 5)
0
2
6
1
e
5
3
4
8
7
11
9
10
0 1 2 3 4 5 6 7 8 9 10 11 12 13
p[e] -3 0 0 5 6 0 -4 4 5 4 6
6
7 11
13
12
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-128
Beispiel für unionByHeight (3)
0
6
2
1
e
5
3
4
8
11
7
10
0 1 2 3 4 5 6 7 8 9 10 11 12 13
p[e] -3 0 0 5 6 0 -4 4 5 4 6
6
7 11
13
9
12
unionByHeight(0, 6)
6
e
0
2
4
1
5
3
Prof. Dr. O. Bittel, HTWG Konstanz
7
11
9
10
0 1 2 3 4 5 6 7 8 9 10 11 12 13
p[e] 6 0 0 5 6 0 -4 4 5 4 6
6
7 11
13
8 12
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-129
Analyse von Union-Find-Strukturen
!  Für Union-Find-Strukturen mit n-Elementen ergeben sich
folgende Laufzeiten:
-  Union: T(n) = O(1)
-  Find: T(n) = O(log n).
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-130
Generierung von Labyrinthen mit einer Union-Find-Struktur (1)
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-131
Generierung von Labyrinthen mit einer Union-Find-Struktur (2)
!  Idee:
Union-Find-Struktur um Erreichbarkeit von Zellen in einem Labyrinth zu speichern:
zwei Zellen des Labyrinths sind genau dann in der gleichen Menge, falls sie
gegenseitig erreichbar sind.
!  Labyrinth
!  Union-Find-Struktur
{ {1, 2}, {3}, {4}, {5, 7, 8, 9, 10, 14, 15}, {6}, {11, 12, 16}, {13},
{17, 18, 19, 23}, {21}, {22}, {24}, {25} }
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-132
Generierung von Labyrinthen mit einer Union-Find-Struktur (3)
!  Algorithmus (1) :
Initialisiere Labyrinth mit n*n Einzelzellen:
Union-Find-Struktur = { {1}, {2}, {3}, {4}, {5}, ... {25} }
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-133
Generierung von Labyrinthen mit einer Union-Find-Struktur (4)
!  Algorihmus (2):
while (Anzahl Mengen in Union-Find-Struktur != 1) {
wähle eine Wand w (keine Außenwand) zufällig aus;
beseitige Wand w, falls dadurch
die beiden Nachbarzellen erreichbar werden;
}
14
19
{ {1, 2}, {3}, {4},
{5, 7, 8, 9, 10, 14, 15}, {6}, {11, 12, 16}, {13},
{17, 18, 19, 23}, {21}, {22}, {24}, {25} }
Prof. Dr. O. Bittel, HTWG Konstanz
{ {1, 2}, {3}, {4},
{5, 7, 8, 9, 10, 14, 15, 17, 18, 19, 23},
{6}, {11, 12, 16}, {13}, {21}, {22}, {24}, {25} }
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-134
Teil III: Graphenalgorithmen
! 
! 
! 
! 
! 
! 
! 
! 
! 
Anwendungen
Definitionen
Datenstrukturen für Graphen
Elementare Algorithmen
Topologisches Sortieren
Kürzeste Wege
Minimal aufspannende Bäume
Flüsse in Netzwerken
Zusammenhangskomponenten
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-135
Problemstellung (1)
Netzwerk:
!  Ein Netzwerk ist ein gewichteter und gerichteter Graph mit zwei speziellen Knoten:
-  Quelle (engl. source): Knoten ohne eingehende Kanten
-  Senke (engl. sink): Knoten ohne ausgehende Kanten.
!  Die Gewichte c(v,w) werden auch als Kapazitäten bezeichnet.
Beispiel:
3
a
3
1
q
2
c
b
2
s
4
2
d
3
Netzwerk mit Quelle q,
Senke s und Kapazitäten.
Anwendungen:
!  Straßenverkehrsnetz:
Kapazitäten geben maximal mögliche Verkehrsdichte je Straße an.
!  Kanalisationsysytem:
Kapazitäten geben maximale Wassermenge an, die je Zeiteinheit durch ein Rohr
fließen kann.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-136
Problemstellung (2)
!  Ein Fluß f in einem Netzwerk G ordnet jeder Kante (v,w) eine Zahl f(v,w) mit folgenden
Eigenschaften zu:
(1)  Kapazitätsbeschränkung: 0 ≤ f(v,w) ≤ c(v,w);
(2)  für jeden Knoten (außer Quelle und Senke) gilt die Erhaltungseigenschaft:
die Summe der eingehenden Flüsse ist
gleich der Summe der ausgehenden Flüsse.
!  Folgerung:
Die Summe der Flüsse, die die der Quelle verlassen, muss gleich der Summe der
Flüsse sein, die an der Senke ankommen.
!  Der Wert eines Flusses ist gleich der Summe der Flüsse, die die Quelle verlassen.
!  Ziel: bestimme den maximalen Fluss eines Netzwerkes.
Genauer: bestimme einen Fluss mit maximalem Wert.
!  Anwendungen:
-  Welchen Verkehrsfluss verkraftet eine Stadt, für die ein Straßenverkehrsnetz
gegeben ist.
-  Welche Wassermenge lässt sich durch eine Kanalisation höchstens
abtransportieren.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-137
Beispiel für Netzwerk mit maximalem Fluss
3/3
q
a
2/3
0/1
2/2
c
Prof. Dr. O. Bittel, HTWG Konstanz
b
2/2
s
1/4
2/2
d
3/3
Fluss / Kapazität
Maximaler Fluss hat den
Wert 5.
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-138
Idee des Algorithmus
Starte mit Null-Fluss, d.h. f(v,w) = 0 für alle Kanten (v,w);
do {
(1) // Erweiterungsweg suchen:
Suche einen Weg p von q nach s, bei dem jede Kante
um einen Fluss Δf vergrößert werden kann;
(2) // Flusserweiterung:
vergrößere für jede Kante (v,w) im Weg p
den Fluss f(v,w) um Δf;
} while (Fluss konnte erweitert werden);
0/3
q
a
0/3
0/1
0/2
c
Prof. Dr. O. Bittel, HTWG Konstanz
b
s
0/4
0/2
Null-Fluss.
0/2
d
0/3
q, a, b, s ist ein möglicher
Erweiterungsweg:
jede Kante kann um den
Fluss Δf = 2 vergrößert werden.
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-139
Problem
!  Die Wahl eines Erweiterungsweges kann in eine Sackgasse führen
(suboptimale Lösung).
!  Beispiel
0/3
q
0/3
a
0/1
0/2
3/3
q
c
s
d
0/2
a
c
0/2
0/4
0/3
0/1
0/2
b
b
0/3
0/2
s
3/4
0/2
d
Erweiterungsweg,
bei dem jede Kante um Δf = 3
vergrößert werden kann.
Nach Flusserweiterung:
Fluss hat nun den Wert 3.
3/3
!  Es gibt nun keinen weiteren Erweiterungsweg, obwohl es eine
Lösung mit einem Flusswert von 5 gibt (siehe S. 136)
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-140
Residualgraphen
!  Lösung des Problems:
Man erlaubt nach der Vergrößerung auch wieder eine Verkleinerung von
Kantenflüssen.
!  Verwalte außer Graph G mit den aktuellen Flüssen zusätzlich einen sogenannten
Residualgraphen Gr (residual = als Rest zurückbleibend).
!  Im Residualgraph wird gespeichert, um welchen Wert der Fluss jeder Kante
noch verändert werden darf (Residualfluss):
(1)  Hat eine Kante (v,w) den Fluss f(v,w) > 0,
dann darf der Fluss verkleinert werden.
Im Residualgraphen Gr wird die Kante (w,v) mit dem Residualfluss f(v,w)
abgespeichert. Beachte: Kante in Gr verläuft in umgekehrter Richtung.
(Flusserhöhung in umgekehrter Richtung verkleinert den Fluss.)
(2)  Hat eine Kante (v,w) den Fluss f(v,w) < c(v,w),
dann darf der Fluss vergrößert werden.
Im Residualgraphen Gr wird die Kante (v,w) mit dem
Residualfluss c(v,w) - f(v,w) abgespeichert.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-141
Beispiel
Residualgraph für Nullfluss
0/3
q
a
0/3
0/1
0/2
b
3
0/2
s
0/4
c
d
0/2
Graph G mit Nullfluss.
a
q
0/3
3
1
2
b
2
s
4
c
d
2
Residualgraph Gr mit
Residualflüssen
3
Residualgraph nach Flusserweiterung
q, a, d, s wird als Erweiterungsweg gewählt. Fluss lässt sich um Δf = 3 vergrößern.
3/3
q
a
0/1
0/2
0/3
b
0/2
s
3/4
c
d
3/3
0/2
Graph G mit Flusswert 3.
Prof. Dr. O. Bittel, HTWG Konstanz
3
q
a
c
Fluss von q nach a darf um
bis zu 3 verringert werden.
b
2
3
1
2
3
s
1
d
2
Residualgraph Gr mit
Residualflüssen
3
Fluss von a nach d darf um
bis zu 1 erhöht und bis zu 3
verringert werden.
Fluss von d nach s darf um
bis zu 3 verringert werden.
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-142
Algorithmus zur Berechnung des maximalen Flusses
Initialisiere Graph G mit Null-Fluss, d.h. f(v,w) = 0 für alle Kanten (v,w);
Initialisiere Residualgraph Gr;
do {
(1) // Erweiterungsweg suchen:
Suche im Residualgraphen Gr einen Weg p von q nach s;
Δf = Minimum aller Residual-Flüsse im Weg p;
(2) 
// Fluss vergrößern:
vergrößere für jede Kante (v,w) im Weg p den Fluss f(v,w) um Δf
(beachte dabei, dass umgekehrte Residualflüsse den Fluss verkleinern);
führe entsprechende Änderungen im Residualgraphen durch;
} while (Fluss konnte vergrößert werden);
2 Ansätze, um einen Erweiterungsweg von q nach s im Residualgraphen zu suchen:
! 
suche den Weg mit dem größten Δf
(wie bei kürzeste Wege in Distanzgraphen; siehe Algorithmus von Dijkstra)
! 
suche Weg mit kleinster Kantenzahl
(wie kürzeste Wege in ungewichteten Graphen durch erweiterte Breitensuche)
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-143
Beispiel (1)
Starte mit Nullfluss
0/3
q
a
0/3
0/1
0/2
c
b
0/2
s
0/4
0/2
d
Graph G mit Nullfluss
Prof. Dr. O. Bittel, HTWG Konstanz
3
0/3
q
a
1
2
c
3
b
2
s
4
d
3
2
Residualgraph Gr mit Residualflüssen
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-144
Beispiel (2)
Suche Weg mit größten Δf im Residualgraphen:
3
a
3
q
b
1
2
2
q, a, d, s ist Weg mit größtem Δf.
s
4
Δf = Minimum der Residual-Flüsse im Weg = 3.
3
c
d
2
Residualgraph Gr
Flusserweiterung um Δf:
a
3/3
q
0/3
0/1
0/2
c
b
0/2
s
3/4
0/2
d
3/3
Graph G mit Flusswert 3
Prof. Dr. O. Bittel, HTWG Konstanz
3
q
a
b
2
1
2
3
c
1
s
3
d
3
2
Residualgraph Gr
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-145
Beispiel (3)
Suche Weg mit größten Δf im Residualgraphen:
a
3
3
b
2
q
1
2
3
1
c
s
d
q, c, d, a, b, s ist Weg mit größtem Δf.
Δf = Minimum der Residual-Flüsse im Weg = 2.
3
2
Residualgraph Gr
Flusserweiterung um Δf:
a
3/3
q
2/3
0/1
2/2
c
b
2/2
3
2/2
s
1/4
d
2
Beachte: Fluss
wurde verkleinert!
3/3
Graph G mit Flusswert 5
q
a
b
2
1
2
1
3
c
1
s
d
3
2
Residualgraph Gr
Ende:
Es gibt im Residualgraphen keine Wege mehr von q nach s. Maximaler Fluss gefunden!
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-146
Analyse und Bemerkungen
!  Analyse ergibt:
T = O(|E|2 log|V|)
(Beweis siehe [Turau 2004]).
!  Bemerkungen:
-  Der hier beschriebene Algorithmus geht zurück auf Ford und Fulkerson (1956).
Analyse von Karp und Edmonds (1972).
-  Man beachte, dass bei einem dichten Graphen (d.h. |E| = O(|V|2) die Laufzeit
anwächst auf T = O(|V|4log|V| )
-  Es gibt inzwischen wesentlich schnellere Algorithmen:
Preflow-Push-Algorithmus von Goldberg, 1985: T = O(|V|3).
Mittels spezieller Datenstrukturen erreichte Goldberg 1988 sogar:
T = O(|E| |V| log(|V|2/|E|).
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-147
Teil III: Graphenalgorithmen
! 
! 
! 
! 
! 
! 
! 
! 
! 
Anwendungen
Definitionen
Datenstrukturen für Graphen
Elementare Algorithmen
Topologisches Sortieren
Kürzeste Wege
Minimal aufspannende Bäume
Flüsse in Netzwerken
Zusammenhangskomponenten
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-148
Zusammenhangskomponenten
!  Ein ungerichteter Graph heißt zusammenhängend (engl. connected), falls es
für jeden Knoten einen Weg zu jedem anderen Knoten gibt.
!  Eine Zusammenhangskomponente eines ungerichteten Graphen G ist ein maximal
zusammenhängender Teilgraph.
!  Ein Knoten heißt Artikulationspunkt (Artikulation med. = Gelenk; engl. articulation
point), wenn sein Wegfall die Anzahl der Zusammenhangskomponenten erhöht.
!  Ein Graph heißt zweifach zusammenhängend (engl. biconnected), falls er keinen
Artikulationspunkt besitzt.
!  Wichtige Problemstellung:
Bestimme in einem ungerichteten Graphen alle Artikulationspunkte.
B
A
D
C
G
!  Ein ungerichteter Graph G mit den
Artikulationspunkten C und D.
H
Prof. Dr. O. Bittel, HTWG Konstanz
F
E
!  G \ {C} zerfällt in 3
Zusammenhangskomponenten
!  G \ {D} zerfällt in 2
Zusammenhangskomponenten.
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-149
Tiefensuchbaum mit Rückwärtskanten (TSB)
!  Stellt man die Tiefensuche graphisch dar, ergibt sich der Tiefensuchbaum.
!  Der Tiefensuchbaum besteht aus Vorwärtskanten.
!  Zusätzlich wird der Tiefensuchbaum ergänzt um Rückwartskanten
Rückwartskanten sind Kanten, die einen Knoten mit einem bereits früher besuchten
Knoten verbinden jedoch nicht den unmittelbar davor besuchten Knoten.
!  Abkürzung TSB für Tiefensuchbaum mit Rückwärtskanten.
A
B
A
B
C
C
D
D
F
G
E
G
E
F
!  TSB, der mit A beginnt
!  Vorwärtskanten und Rückwärtskanten
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-150
Vorfahre und Rückwärtsweg
!  Knoten v ist Vorfahre von w, falls es im TSB einen Weg
von v nach w gibt, der nur aus Vorwärtskanten besteht.
!  Ein Rückwärtsweg ist ein Weg in einem TSB
mit einer beliebig langen Folge von Vorwärtskanten
und dann genau einer Rückwärtskante.
A
B
C
D
E
F
Prof. Dr. O. Bittel, HTWG Konstanz
Beispiele:
!  A, B, C sind Vorfahren von D und von G
G
!  E – F – D ist ein Rückwärtsweg
!  B – C – D – A ist ein Rückwärtsweg
!  E – F – D – A ist kein Rückwärtsweg
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-151
Bestimmung von Artikulationspunken
(W)
Die Wurzel ist ein Artikulationspunkt,
wenn sie mehr als ein Kind hat.
(NW)
Ein Knoten v ≠ Wurzel ist ein Artikulationspunkt, falls v im TSB ein Kind hat,
von dem es keinen Rückwärtsweg zu einem Vorfahren von v gibt.
B
C
A
B
A
B
G
A
C
D
C
F
D
G
E
E
F
G
E
F
!  TSB, der mit Knoten C beginnt
!  TSB, der mit Knoten A beginnt
!  C ist Artikulationspunkt wegen (W)
!  C und D sind Artikulationspunkte wegen Regel (NW)
!  D ist Artikulationspunkt wegen (NW)
Prof. Dr. O. Bittel, HTWG Konstanz
D
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-152
Zusätzliche Nummerierungen im TSB
!  TSNr[v]:
gibt für jeden Knoten v den Besuchszeitpunkt bei der Tiefensuche an.
!  MinNr[v]: die kleinste TSNr eines Knoten w,
der von v über einen Rückwärtsweg erreicht werden kann.
Falls es von v keine Rückwärtswege gibt, ist MinNr[v] = TSNr[v].
A 1,1
B
A
B 2,1
C 3,1
C
D
D 4,1
F
G 7,7
E 5,4
G
E
F 6,4
TSB mit TSNr und MinNr
!  Regel (NW) lässt sich damit wie folgt umsetzen:
Knoten v (außer Wurzel) ist genau dann ein Artikulationspunkt,
wenn v ein Kind w hat mit MinNr[w] ≥ TSNr[v] .
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-153
Berechnung von MinNr
!  MinNr[v] = Minimum von
(M1)
TSNr[v],
(M2)
kleinster Wert von TSNr[w] für alle Rückwärtskanten (v,w)
(M3)
kleinster Wert von MinNr[w] für alle Vorwärtskanten (v,w)
B
A 1,1
A
B 2,1
C 3,1
C
D
G
F
E
D 4,1
E 5,4
F 6,4
Prof. Dr. O. Bittel, HTWG Konstanz
G 7,7
TSB mit TSNr und MinNr
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-154
Algorithmus zur Bestimmung von Artikulationspunkten
boolean[ ] visited;
int counter;
int[ ] TSNr[n];
int[ ] MinNr;
int[ ] p;
public void findArtPoint(Graph g) {
visited mit false initialisieren;
counter = 1;
Wähle beliebigen Knoten v;
findArtPoint(g, v);
}
Globale Variablen:
!  counter dient zum Hochzählen der
Besuchszeitpunkte.
!  TSNr und MinNr wie soeben beschrieben.
!  p[v] speichert den im TSB zu
v gehörenden Elternknoten ab.
!  findArtPoint(g) gibt alle Artikulationspunkte
des ungerichteten Graphen g aus.
private void findArtPoint(Graph g, Vertex v) {
!  findArtPoint(g,v) startet eine rekursive
visited[v] = true;
Tiefensuche bei Knoten v.
TSNr[v] = counter++;
!  Beachte:
MinNr[v] = TSNr[v]; // (M1)
Die Prüfung, ob die Wurzel ein
for (jeden Nachbarn w von v )
Artikulationspunkt ist (Regel (W)),
if (! visited[w]) {
wurde einfachheitshalber weggelassen.
p[w] = v;
findArtPoint(g, w);
if (MinNr[w] >= TSNR[v]) println(v + “ist ein Artikulations-Punkt”);
MinNr[v] = Minimum(MinNr[v], MinNr[w]); // (M3)
}
else if (p[v] != w) // (v,w) ist Rückwärtskante
MinNr[v] = Minimum(MinNr[v], TSNr[w]); // (M2)
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Graphenalgorithmen
SS 2017
3-155
Herunterladen