© Michael Philippsen

Werbung
16. Graphalgorithmen
16.1
16.2
16.3
16.4
16.5
16.6
16.7
16.8
16.9
16.10
16.11
16.12
Algorithmen und Datenstrukturen
16. Graphalgorithmen
Prof. Dr. Christoph Pflaum
Department Informatik • Martensstraße 3 • 91058 Erlangen
Graph-Grundlagen
Darstellung von Graphen im Rechner
Euler-Pfad
Graph-Traversierung
Topologische Sortierung
Kürzeste Pfade
Minimaler Spannbaum
Transitive Hülle
Matching
Kantenfärbung
Eckenfärbung
Zusatz: Theorie ebener Graphen
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-2
16.1 Graph-Grundlagen
16.1 Graph-Grundlagen
Ein gerichteter Graph ist ein Paar G=(V,E), wobei
Ein ungerichteter Graph ist ein Graph G=(V,E) für den gilt:
ƒ V eine endliche Menge von Knoten („vertex, vertices“) und
ƒ E eine zweistellige Relation auf V ist, d.h. E f VHV.
Die Elemente von E werden (gerichtete) Kante („edge“) genannt.
Für alle vi,vj 0V: (vi,vj) 0 E Y (vj,vi) 0 E.
ƒ E ist symmetrisch.
ƒ In der grafischen Darstellung gehört zu jedem Pfeil ein Pfeil in die
Gegenrichtung. Statt Pfeilen zeichnet man daher spitzenlose
Linien.
ƒ In der Mengenschreibweise schreibt man [vi,vj] für beide Paare.
reflexive Kante, Schlinge
Übliche grafische Darstellung:
Beispiel:
v2
V={v1,v2,v3,v4,v5,v6,v7}
v1
E={(v1,v2),
(v2,v3), (v2,v6), (v2,v7),
(v5,v3), (v5,v7),
v4
v3
(v6,v5),
v5
(v7,v6), (v7,v7)}
Knoten
v7
v6
(gerichtete) Kante
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-3
© Michael Philippsen
Beispiel:
V={v1,v2,v3,v4,v5,v6,v7}
E={[v1,v2],
[v2,v3], [v2,v6], [v2,v7],
[v5,v3], [v5,v7],
[v6,v5],
[v7,v6], [v7,v7]}
Übliche grafische Darstellung:
v2
v1
v3
v7
v6
v4
v5
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-4
16.1 Graph-Grundlagen
16.1 Graph-Grundlagen
ƒ Ein Pfad (Weg, Kantenzug) von x nach y ist eine endliche
Folge von Knoten x=a0, a1, …, ap=y wobei (ai, ai+1)0E.
ƒ Pfade im ungerichteten Graphen:
□ p ist Anzahl der Kanten im Pfad und heißt Länge des Pfades.
□ Der Pfad verbindet x und y,
□ y ist von x aus erreichbar.
vier (einfache) Pfade von v1 nach v5
v1
v5
ƒ In einem einfachen Pfad kommt jeder Knoten höchstens einmal
vor.
Beachte: In unserer
Graph-Definition kann es
keine "parallelen Kanten
(mit gleicher Richtung)"
zwischen zwei Knoten
geben. Sonst „Multigraph“
ƒ Pfade im gerichteten Graphen:
zwei (einfache) Pfade von v1 nach v5
v1
v5
„nicht gegen die Pfeilrichtung“
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-5
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-6
16.1 Graph-Grundlagen
16.1 Graph-Grundlagen
ƒ Ein Pfad der Länge p≥1 von x nach x heißt Zyklus
(von x nach x).
ƒ Ein Zyklus von x nach x heißt minimaler Zyklus (von x nach x),
wenn außer x kein anderer Knoten mehr als einmal vorkommt.
ƒ Ein Graph G=(V,E) heißt (stark) zusammenhängend
(oder stark verbunden), wenn es für alle x,y0V einen
Pfad von x nach y gibt.
□ Jeder Knoten ist von jedem anderen Knoten aus erreichbar.
ungerichteter Beispielgraph:
v7
v6
v5
Zyklen:
ƒ v5, v7, v6, v5
ist minimal
ƒ v5, v7, v7, v7, v6, v5
ist nicht minimal
ƒ v7, v7 ist minimal
ƒ Gibt es mehr minimale
Zyklen?
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-7
© Michael Philippsen
gerichteter Beispielgraph:
X
X
X
X
X
X
Graph ist nur ohne diesen
Knoten zusammenhängend.
Diese Knoten sind nicht von
jedem anderen Knoten aus
erreichbar.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-8
16.1 Graph-Grundlagen
16.1 Graph-Grundlagen
ƒ Ein gerichteter Graph heißt schwach zusammenhängend,
wenn der zugehörige ungerichtete Graph (der durch Hinzunahme
aller Rückwärtskanten entsteht) zusammenhängend ist.
ƒ Ein stark zusammenhängender ungerichteter Graph heißt
Baum, wenn es keine Schlingen gibt und wenn es zwischen je
zwei verschiedenen Knoten genau einen einfachen Pfad gibt.
Im gerichteten Graphen gibt es
keinen Pfad zu diesem Knoten.
gerichteter Beispielgraph
(nicht stark zusammenhängend)
zugehöriger ungerichteter Graph
ist zusammenhängend:
Weglassen von Kanten kann Bäume erzeugen:
Ein Knoten, zu dem
nur eine Kante
führt, heißt Blatt.
Der gerichtete Graph ist daher
schwach zusammenhängend.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-9
…
Später folgt: Definition von Bäumen in gerichteten Graphen.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-10
16.1 Graph-Grundlagen
16.1 Graph-Grundlagen
ƒ Ein Graph G'=(V',E') ist ein Teilgraph (Untergraph) eines
Graphen G=(V,E) genau dann, wenn V' ⊆ V und E' ⊆ E.
ƒ Sind G ein Graph und R ein zyklenfreier Teilgraph
von G, der alle Knoten von G enthält, und sind G und R
beide zusammenhängend, dann heißt R Spannbaum
(aufspannender Baum) von G.
Ein Teilgraph muss
diese Kante nicht
enthalten, wohl
aber ein induzierter
Teilgraph.
Einige Spannbäume:
ƒ Ein Graph G'=(V',E') ist ein induzierter Teilgraph eines
Graphen G = (V,E) gdw V' ⊆ V und E' = { (u,v) ∈ E | u,v ∈ V' }
Wähle einige Knoten aus G aus; es „überleben“ in E'
alle Kanten, die diese ausgewählten Knoten verbinden.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-11
© Michael Philippsen
…
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-12
16.1 Graph-Grundlagen
16.1 Graph-Grundlagen
ƒ Eine Familie Gi=(Vi,Ei) mit i ∈ {1,…,n} von Teilgraphen heißt
Partitionierung eines Graphen G = (V,E) gdw
1. jeder Graph Gi zusammenhängend ist und
2. ∀i,j ∈ {1,…,n}, i≠j Y Vi ∩Vj = ∅ und
3. Ui=1...n Vi = V.
ƒ Jeder Graph Gi wird Komponente von G genannt.
Partitionierung in 3 Komponenten
ƒ Eine Zusammenhangskomponente Z eines Graphen ist ein
zusammenhängender Teilgraph von G, der in keinem anderen
zusammenhängenden Teilgraphen von G enthalten ist. Z ist also
ein maximaler zusammenhängender Teilgraph von G.
Folgender Graph hat zwei Zusammenhangskomponenten:
ƒ Jeder Graph kann in eindeutiger Weise in die Menge seiner
Zusammenhangskomponenten partitioniert werden.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-13
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-14
16.1 Graph-Grundlagen
16.1 Graph-Grundlagen
ƒ Ist (x,y)0E, so heißen
ƒ Die Anzahl der direkten Vorgänger eines Knotens heißt
Eingangsgrad des Knotens. Die Anzahl der direkten Nachfolger
eines Knotens heißt Ausgangsgrad des Knotens. Bei
ungerichteten Graphen spricht man vom Grad des Knotens.
□ x direkter Vorgänger (bei gerichteten Graphen auch: Elternknoten)
von y.
□ y direkter Nachfolger (bei gerichteten Graphen auch: Kind) von x.
□ Man schreibt x → y.
□ Die Kante (x,y) heißt inzident zu x (bzw. zu y).
ƒ Falls ein Pfad von x nach z führt, so heißen
□ x Vorgänger (bei gerichteten Graphen auch: Vorfahr) von z,
□ z Nachfolger (bei gerichteten Graphen auch: Nachkomme) von x.
□ Man schreibt x →* z
A
B
C
E
D
F
A ist Vorfahr von allen anderen Knoten
C und D sind Kinder von B
B und E sind Eltern von D
F ist Nachkomme von A und von E
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-15
© Michael Philippsen
ungerichteter Beispielgraph:
4
1
Grad
2
3
gerichteter Beispielgraph:
1/3
0/1
3/2
0
3
3
Minimalgrad d. Graphen: 0
Maximalgrad d. Graphen: 4
2/1
2/0
1/2
Eingangs-/Ausgangsgrad
Knoten mit Grad 0/* heißen Quelle,
Knoten mit Grad */0 heißen Senke.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-16
16.1 Graph-Grundlagen
16.1 Graph-Grundlagen
ƒ Ein stark zusammenhängender ungerichteter Graph heißt
Baum, wenn es keine Schlingen gibt und wenn es zwischen je zwei
verschiedenen Knoten genau einen einfachen Pfad gibt.
ƒ Bei gerichteten Graphen sind mehr Begriffe nötig:
ƒ Bei gerichteten Graphen ist ein Baum ein Wurzelgraph, in
dem zu jedem Knoten genau ein (eindeutiger) Pfad von der
Wurzel aus führt.
Baum:
□ Gerichteter azyklischer Graph (DAG, „directed acyclic graph“): Gerichteter
Graph ohne Zyklen.
□ Ein Knoten v eines DAG heißt Wurzel, falls es keine auf ihn gerichte-ten
Kanten gibt. Hat ein DAG nur eine Wurzel, so heißt er Wurzelgraph.
Wurzelgraph:
DAG:
Wurzel
X
X
Es gibt noch andere
Möglichkeiten, um durch
Entfernen von Kanten
aus dem Graphen einen
Baum zu machen.
X
Wurzel
Wurzel
Nur zyklenfrei, wenn X entfernt ist.
Wurzel
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-17
ƒ Ein DAG mit mehreren Wurzeln, aber eindeutigen Pfaden, heißt
Wald.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-18
16.1 Graph-Grundlagen
16.1 Graph-Grundlagen
ƒ Aus dieser Baum-Definition folgt:
Weitere Baum-Begriffe (1)
ƒ Binärbaum = Baum mit Grad 2
□ Es gibt (auch bei gerichteten Graphen) keinen Zyklus.
□ Die Wurzel ist der einzige Knoten ohne direkten Vorgänger.
□ Jeder andere Knoten hat genau einen direkten Vorgänger, er
kann aber beliebig viele direkte Nachfolger haben.
ƒ Abweichend zu ungerichteten Graphen definiert man:
□ Der Grad eines Knotens ist die Anzahl der Nachfolger eines
Knotens.
(Es werden also nur die Ausgangskanten berücksichtigt.)
□ Der Grad des Baums ist der maximale Grad seiner Knotens.
□ Ein Knoten mit Grad 0, also ohne Nachfolger, heißt Blatt.
□ Alle anderen Knoten heißen innere Knoten des Baums.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-19
© Michael Philippsen
□ Jeder Knoten hat maximal 2 Nachfolger.
□ Man spricht vom rechten/linken Kind (Nachfolger, Sohn).
□ Wenn bei der grafischen Darstellung von Bäumen klar ist,
welcher Knoten die Wurzel ist (und welche Bedeutung die
Kante zwischen Elternknoten und Kinderknoten hat), kann man
auf die Pfeilspitzen verzichten.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-20
16.1 Graph-Grundlagen
16.1 Graph-Grundlagen
Weitere Baum-Begriffe (2)
Am Beispiel:
ƒ Der Unterbaum eines Knotens v im Baum sind v und alle
nachfolgenden Knoten plus die verbindenden Kanten.
1:2
0:1
□ Unterbäume sind wieder Bäume.
innerer Knoten
2:0
ƒ Triviale Bäume: leerer Graph; kantenloser Graph mit nur einem
Knoten.
Wurzel
ƒ Die Länge des Pfades von der Wurzel zu einem Knoten k
bestimmt die Höhe von k im Baum.
16.1 Graph-Grundlagen
Beispiele:
7
Andere Darstellungsformen von Bäumen
ƒ Kontour-Darstellung
▪ Einrückungsdarstellung
Stammbaum
A
B
Cecilia
Claude
H
Elisabeth
3
Victoria
Loius
Alice
Olga
Georg I
Andrew
J
D
B
Elisabeth II
Mary
Georg VI
George V
-
0:1
2:0
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-22
16.1 Graph-Grundlagen
5
Unterbaum dieses Knotens:
1:1
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-21
*
3:1
n:m = Höhe:Grad
Grad des Baums: 2
Bei binären Suchbäumen andere Höhen-Definition: Blätter
haben die Höhe 0, Höhe eines Knotens = Länge des längsten
Pfades zu einem Blatt.
Ausdrucksbaum
„Kantorowitsch-Baum“ 5 * (7-3)
4:0
Blatt
□ Die Wurzel hat die Höhe 0.
□ Die Höhe aller direkten Nachfolger eines Knotens v ist um 1 größer
als die Höhe von v.
Blatt
2:1
A
F
C
E
Charles
Philip
H
J
G
C
D
E
G
F
ƒ Listendarstellung
A(B(HJ)C( DE( G)F ))
ist Kind von
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-23
© Michael Philippsen
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-24
16.1 Graph-Grundlagen
16.2 Darstellung von Graphen im Rechner
ƒ Ein Graph G=(V,E) wird zu einem bewerteten/gewichteten
Graphen, indem man eine Gewichtsfunktion gw:EÆù bzw.
gw:EÆú>0 ergänzt, die jeder Kante e0E ein positives Gewicht
gw(e) zuordnet.
ƒ Die (bewertete) Länge c(w) eines Pfades w definiert man in
gewichteten Graphen als die Summe der Gewichte seiner Kanten:
für w = (x=a0, a1, …, ap=y) ist
p-1
c(w) = 3 gw(ai,ai+1)
2
Grafische Darstellung:
3
1
4
Mengenschreibweise:
G = (V,E) mit x
y ⇒ (x,y) ∈ E ⊆ V×V.
V={1,2,3,4} E={(1,2), (1,4), (2,3), (2,4), (3,1), (4,4)}
i=0
ƒ Statt von (bewerteter) Länge spricht man auch von Kosten
(„cost“) des Pfades w.
Matrixdarstellung:
(Adjazenzmatrix)
ƒ Beispiel: Städte mit Straßennetz und Transportkosten (oder Länge
der Straße.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-25
boolean-Werte: true (1)
bedeutet, dass es eine
Kante von 2 nach 4 gibt.
1 2 3 4
1
2
3
4
0
0
1
0
1
0
0
0
0
1
0
0
1
1
0
1
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-26
16.2 Darstellung von Graphen im Rechner
16.2 Darstellung von Graphen im Rechner
Adjazenzmatrix
Adjazenzmatrix
2
1
3
4
boolean [][] kanten = {
{false, true, false,
{false, false, true,
{true, false, false,
{false, false, false,
}
true},
true},
false},
true}
ƒ Bei ungerichteten Graphen sind Adjazenzmatrizen symmetrisch,
es reicht die Speicherung einer Dreiecksmatrix.
ƒ Graphen ohne Schlingen haben in der Diagonalen stets „false“.
ƒ Graphen mit wenigen Kanten führen zu spärlich besetzten
Matrizen.
ƒ Bei bewerteten/gewichteten Graphen speichert man statt booleanWerten die Kantengewichte in der Matrix. Fehlende Kanten durch
ein Gewicht nahe 4 ausgedrückt.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-27
© Michael Philippsen
K
A
R
L
S
R
U
H
E
Entfernungstabelle
ist die bekannteste
Form der Adjazenzmatrix
KARLSRUHE
-
L
E
I
P
Z
I
G
M
Ü
N
C
H
E
N
N
Ü
R
N
B
E
R
G
400 300 300
LEIPZIG
400
-
MÜNCHEN
300 250
NÜRNBERG
300 150 100
STUTTGART
70 450 230 300
S
T
U
T
T
G
A
R
T
70
250 150 450
-
100 230
-
300
-
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-28
16.2 Darstellung von Graphen im Rechner
Adjazenzlisten:
knoten[1]
knoten[2]
knoten[3]
knoten[4]
=
=
=
=
new
new
new
new
1
2
3
4
2
3
1
4
Knoten(1,
Knoten(2,
Knoten(3,
Knoten(4,
4
4
new
new
new
new
16.2 Darstellung von Graphen im Rechner
1
4
3
Verbindung(2, new Verbindung(4));
Verbindung(3, new Verbindung(4));
Verbindung(1));
Verbindung(4));
Bei gewichteten Graphen hat das Verbindungsobjekt
Instanzvariablen für die Speicherung des Gewichts.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-29
16.2 Darstellung von Graphen im Rechner
Grafik:
+ sehr leicht lesbar für Menschen
- extrem kompliziert für maschinelle Bearbeitung
Mengen:
Nachfolger von Knoten 1
sind die Knoten 2 und 4.
2
+ geeignet für einige mathematische Operationen
- teure Suche nach Knoten und Kanten
Als Reihung:
5
7
9
10
2
4
3
1
2
3
4
5
6
7 8
Kanten
Knoten
+ sehr kompakte Darstellung (wenig Speicher), O(|V|+|E|)
- kostspielige Suche nach Kanten, O(|E|)
Reihung:
+ noch kompakter als Liste
- aufwändige Änderungen (Einfügen/Löschen)
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-31
© Michael Philippsen
1
4
4
9 10
Die Nachfolger von
Knoten 1 finden sich in
der Reihung ab
Position 5 (und bis <7).
Bei gewichteten Graphen hat die Reihung zwei „Zeilen“.
Gewichte im Kantenteil der 2. Zeile speichern.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-30
16.3 Euler-Pfad
Königsberger Brückenproblem:
Gibt es einen geschlossenen
Pfad, der über alle 7 Brücken
führt?
Adjazenzmatrix:+ sehr schnell feststellbar, ob eine Kante existiert, O(1)
+ manche Graphenoperationen lassen sich als
Matrizenoperationen darstellen
- Speicherverschwendung bei dünnen Graphen, O(n²)
Adjazenzliste:
4
2
A
D
C
B
Zeichenproblem:
Kann das Häuschen mit
einem Strich gezeichnet
werden?
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-32
3
16.3 Euler-Pfad
16.4 Graph-Traversierung
ƒ Eulerscher Pfad (Eulerweg, offener Eulerzug):
Pfad, der alle Kanten umfasst und jede genau einmal durchläuft.
ƒ Eulerscher Zyklus (Eulertour, Eulerkreis, geschl. Eulerzug):
Eulerscher Pfad, bei dem Start- und Endknoten identisch sind.
A
als Multigraph, da
parallele
Kanten
D
C
B
A
D
C
B
ƒ Die Frage, ob ein Eulerscher Zyklus existiert, ist leicht zu beantworten.
ƒ Idee: Wenn man in einen Knoten kommt, muss man auf anderem
Weg wieder herauskommen.
ƒ Euler zeigte: Es existiert ein Euler-Zyklus gdw. der Grad jedes
Knotens durch 2 teilbar ist und der Graph zusammenhängend ist.
(Beweisrichtung Æ leicht, Beweisrichtung Å Übung)
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-33
ƒ Bisher war die Bearbeitung der Eingabe immer einfach:
man ist sequentiell vorgegangen und hat ein Eingabe-Element
nach dem anderen betrachtet.
ƒ Aber bei Graphen? Traversierung von Graphen, die jeden Knoten
einmal betrachten, ist eine Aufgabe für sich, die in vielen GraphAlgorithmen bewältigt werden muss.
□ Gegeben: Graph G
□ Gesucht: Besuch jedes Knotens
ƒ Lösungswege:
□ Tiefensuche
□ Breitensuche
□ Bei Bäumen können spezielle Verfahren angewendet werden, weil
keine Zyklen vorliegen.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-34
16.4 Graph-Traversierung
16.4 Graph-Traversierung
Grundidee der Tiefensuche (DFS, „depth-first-search“)
Grundidee der Tiefensuche (DFS, „depth-first-search“)
ƒ Besuche zuerst die Kinder jedes Knotens, Absteigen bis zum
Blatt, dann zum nächsten Blatt, …
ƒ Besuch eines Museums mit vielen Gängen, wobei man jeden
Gang ablaufen möchte.
ƒ Verfahren:
2
13
5
7
3
11
mögliche Tiefensuch-Reihenfolgen:
2, 13, 5, 1, 3, 7, 11
2, 13, 3, 5, 1, 7, 11
2, 7, 11, 13, 5, 1, 3
2, 7, 11, 13, 3, 5, 1
1
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-35
© Michael Philippsen
□ Man läuft in das Museum hinein.
□ Man betritt einen neuen Gang, sobald er sich öffnet. Wenn sich mehrere
Gänge gleichzeitig öffnen, wählt man einen (meist den linken)
□ und hinterlässt an der Kreuzung einen Kieselstein.
□ Wenn man an eine Kreuzung stößt, die bereits einen Kiesel hat, kehrt man
um und geht den gleichen Weg zurück bis zur vorhergehenden Kreuzung.
(Gänge und Kreuzungen können mehrfach betreten werden.)
□ Wenn von dieser noch ein unausprobierter Gang abgeht, wird dieser
erforscht.
□ Gibt es keinen unausprobierten Gang mehr, muss weiter zurück gegangen
werden.
ƒ Ariadne benutzte ein Garnknäuel, um Theseus die Rückkehr aus
dem Labyrinth zu ermöglichen, in dem er den Minotaurus getötet
hat.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-36
16.4 Graph-Traversierung
16.4 Graph-Traversierung
Tiefensuche am Beispiel
Rekursive DFS-Implementierung
Speicherung als Adjazenzliste:
Reihenfolge nicht festgelegt.
Startknoten
X
X
4
2
X
1
X
5
3
Die Markierung dient dazu, dass der Algorithmus bei
allgemeinen Graphen nicht in Endlosschleifen läuft.
Es wird nur die Zusammenhangskomponente besucht,
in der sich der Startknoten befindet.
void DFS(Graph G, Node v) {
v.mark();
Iterator iter = v.getEdges();
while (iter.hasNext()) {
Edge e = (Edge) iter.next();
if (e.target.isUnmarked()) {
DFS(G, e.target);
}
}
}
Jeder Knoten einer
Zusammenhangskomponente
wird erreicht: sonst gäbe es
einen Knoten ohne
Markierung, der über eine
Kante mit einem markierten
Knoten verbunden ist. Das
kann nicht vorkommen.
Erweiterung für nicht zusammenhängende Graphen:
wiederhole beginnend bei unmarkiertem Knoten
ƒAufwand O(|V|+|E|):
ƒ jede der |E| Kanten wird (höchstens) von beiden Seiten betrachtet
ƒ zusätzlich gibt es ggf. isolierte Knoten in V
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-37
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-38
16.4 Graph-Traversierung
16.4 Graph-Traversierung
Rekursion am Beispiel:
Besuch der Nachfolger von links nach rechts
Keller zur Implementierung der Rekursion
Besuch der Nachfolger von links nach rechts
DFS(2)
DFS(13)
2
13
DFS(5)
7
DFS(2) DFS(13) DFS(5)
DFS(7)
DFS(3)
DFS(7)
2
13
DFS(1)
7
DFS(1)
DFS(3)
DFS(7)
DFS(3)
5
3
11
DFS(7)
DFS(11)
1
5
3
11
DFS(3)
DFS(7)
DFS(7)
1
while-Schleife über die
Kanten, die von 2 abgehen,
hier v.l.n.r.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-39
© Michael Philippsen
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-40
DFS(11)
16.4 Graph-Traversierung
Iterative DFS-Implementierung mit Keller
void DFS(Graph G, Node v) {
Keller k = new Keller();
k.push(v); //merke Wurzel für Besuch vor
while (!k.isEmpty()) {
//besuche oberstes Kellerelement
v = k.top(); k.pop();
v.mark();
Iterator iter = v.getEdges();
while (iter.hasNext()) {
//lege alle Nachfolger auf Keller
Edge e = (Edge) iter.next();
if (e.target.isUnmarked()) {
k.push(e.target);
}
}
}
}
16.4 Graph-Traversierung
Die Kinder eines
Knotens v kommen
oben auf den
Keller. Ein Kind k
wird als erstes
besucht. Dessen
Kinder kommen
wieder oben auf
den Keller …
Erst wenn alle
Enkel (und deren
Nachfolger) besucht wurden,
kommen die
Geschwister von
k an die Reihe …
Æ Tiefensuche
(Rekursive) DFS-Implementierung mit Nutzarbeit
void DFS(Graph G, Node v) {
v.mark();
//präKnotenArbeit(v);
Iterator iter = v.getEdges();
while (iter.hasNext()) {
Edge e = (Edge) iter.next();
if (e.target.isUnmarked()) {
//inKnotenArbeit(v);
DFS(G, e.target);
//kantenArbeitif(e)
}
//kantenArbeitimmer(e);
}
//postKnotenArbeit(v);
}
Aufgabe: ergänzen Sie Nutzarbeit
in iterativer Version
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-41
16.4 Graph-Traversierung
Alternative: Die
Iteratoren über die
Kinder werden auf
den Keller gelegt.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-43
© Michael Philippsen
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-42
16.4 Graph-Traversierung
Zweite Iterative DFS-Implementierung mit Keller
void DFS(Graph G, Node v) {
Keller k = new Keller();
v.mark();
Iterator iter = v.getEdges();
k.push(iter);
while (!k.isEmpty()) {
iter = k.top();
if(iter.hasNext()) {
Edge e = (Edge) iter.next();
if (e.target.isUnmarked()) {
e.target.mark();
iter = e.target.getEdges();
k.push(iter)
}
}
else k.pop();
}
}
Graph wird besucht, um
eine bestimmte Aufgabe
zu erledigen. Abhängig
von der Aufgabe sind zu
konkretisieren:
ƒ präKnotenArbeit: was ist
beim ersten Betreten eines
Knotens zu tun?
ƒ inKnotenArbeit: was ist
zwischen dem Besuchen
der Nachfolger zu tun?
ƒ postKnotenArbeit: was ist
nach Besuch aller Nachfolger zu tun?
ƒ kantenArbeit: was ist beim
Abstieg zu tun?
DFS-Anwendungsbeispiel: Knotenzahl des Unterbaums
ƒ Gegeben: Baum G
ƒ Gesucht: Bestimmt für jeden Knoten v die Anzahl der Knoten
des Unterbaums, der v als Wurzel hat.
8
6
1
3
1
2
1
In Bäumen gibt
es keine bereits
markierten
Nachfolger.
1
void DFSKnotenZahl(Graph G, Node v) {
v.mark();
v.zaehler = 1; //präKnotenArbeit(v)
Iterator iter = v.getEdges();
while (iter.hasNext()) {
Edge e = (Edge) iter.next();
if (e.target.isUnmarked()) {
DFSKnotenZahl(G, e.target);
}
//kantenArbeitimmer(e):
v.zaehler += e.target.zaehler;
}
}
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-44
16.4 Graph-Traversierung
16.4 Graph-Traversierung
DFS-Anwendungsbeispiel: Knotenzahl des Unterbaums
B
D
F
1
3
1
DFS-Anwendungsbeispiel: Ausdrucksbaum
ƒ Betrachte den Ausdruck 5 * (7-3)
A.zaehler = 1;
A
B.zaehler = 1;
C
A.zaehler += B.zaehler // = 2
C.zaehler = 1;
E
D.zaehler = 1;
G
H
F.zaehler = 1;
D.zaehler += F.zaehler // = 2
G.zaehler = 1;
D.zaehler += G.zaehler // = 3
C.zaehler += D.zaehler // = 4
8
E.zaehler = 1;
6
H.zaehler = 1;
2
E.zaehler += H.zaehler // = 2
C.zaehler += E.zaehler // = 6
1
1
A.zaehler += C.zaehler // = 8
□ Darstellung als Ausdrucksbaum:
*
-
5
enh
amm
Zu s
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-45
?
ang
7
3
□ Infix-Form:
Operanden
□ Postfix-Form:
5 * (7-3)
//Operatoren zwischen den
573-*
□ Präfix-Form:
*5-73
//Operatoren hinter den Operanden
//siehe „Auswertung mit Keller“
//Operatoren stehen vor Operanden
//“polnische Notation“
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-46
16.4 Graph-Traversierung
16.4 Graph-Traversierung
DFS-Anwendungsbeispiel: Ausdrucksbaum
DFS-Anwendungsbeispiel: Ausdrucksbaum
ƒ
ƒ
Tiefensuche durch Ausdrucksbaum, dabei Nachfolger v.l.n.r.
Textausgabe in Infix-/Inorder-Form (nur Binärbäume)
4.: „*“
1. präKnotenArbeit: Ggf. „(“ drucken
2. Besuche linken Unterbaum
1.
3. inKnotenArbeit: Drucke
Knotensymbol
5
4. Besuche rechten Unterbaum
5. postKnotenArbeit: Ggf. „)“ 2.:„5“
*
3.
5.
6.:„(“
2. postKnotenArbeit: Drucke
Knotensymbol
-
7.
7
Zahlen geben die Reihenfolge an, in
„“ steht ausgegebener Text
ƒ Tiefensuche durch Ausdrucksbaum, dabei Nachfolger v.l.n.r.
ƒ Textausgabe in
Postfix-/Postorder-Form
*
1. Besuche Unterbäume (v.l.n.r.)
1.
8.:„7“
3
Solange „links-abwärts“ wie
möglich. Rechtes Kind wird
erst besucht, wenn der
linke Unterbaum völlig
abgearbeitet ist.
5
11.:„-“
4.
3.
-
2.:„5“
5.
7
7.
8.
6.:„7“
erzeugte lineare Liste: 5 7 3 - ...
erzeugte lineare Liste: 5 * ( 7 - 3 ...
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-47
© Michael Philippsen
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-48
3
9.:„3“
16.4 Graph-Traversierung
16.4 Graph-Traversierung
DFS-Anwendungsbeispiel: Ausdrucksbaum
Inorder-Besuch eines binären Suchbaums liefert sortierte Liste
ƒ Tiefensuche durch Ausdrucksbaum, dabei Nachfolger v.l.n.r.
ƒ Textausgabe in
1.:„*“
Präfix-/Präorder-Form
*
1. präKnotenArbeit: Drucke
2.
Knotensymbol
2. Besuche Unterbäume (v.l.n.r.)
5
4.
5.
6.:„-“
3.:„5“
Solange „links-abwärts“ wie
möglich. Rechtes Kind wird erst
besucht, wenn der linke
Unterbaum völlig abgearbeitet ist.
26
= drucke Wert
94
41
-
7.
7
DFS mit
inKnotenArbeit
99
27
9.
54
97
10.
8.:„7“
36
3
43
65
39
11.:„3“
57
78
92
erzeugte lineare Liste: * 5 - 7 3
26
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-49
27
36
39
41
43
54
57
65
78
16.4 Graph-Traversierung
ƒ Beim Besuchen von Binärbäumen kann man die Tiefensuche
iterativ implementieren, ohne einen expliziten Keller zu benötigen.
ƒ Idee:
Tiefensuche-Iterator für Inorder-Besuch
ƒ Wenn man von links unten kommt, dann muss der rechte Nachfolger
(und dessen Nachfahren) noch besucht werden.
ƒ Wenn man von rechts kommt, geht es weiter Richtung Wurzel zurück.
ƒ Auf den folgenden Folien wird ein entsprechender Iterator für
einen Binärbaum entwickelt, der bei next() den nächsten Knoten
gemäß DFS-Reihenfolge liefert.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-51
© Michael Philippsen
94
97
99
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-50
16.4 Graph-Traversierung
□ Die zwei Nachfolger sind ohnehin im Knoten gespeichert und können
v.l.n.r. abgefragt werden.
□ Wenn man die aktuelle Besuchsposition kennt, kann man beim
Rückkehren von unten erkennen, ob man von links unten oder von
rechts unten kommt.
92
ƒ Zustand des Iterators muss im Iterator-Objekt gespeichert werden:
Instanzvariable position zeigt jeweils auf dasjenige Element,
das beim nächsten next()-Aufruf geliefert werden soll.
ƒ Initialisierung: Knoten "ganz links" im Baum
position = treeSet.root;
if (position != null) {
while (position.left != null) {
position = position.left;
}
}
position
An der Stelle position geht es nicht mehr nach links weiter,
position hat (wenn überhaupt) einen rechten Nachfolger.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-52
16.4 Graph-Traversierung
16.4 Graph-Traversierung
Tiefensuche-Iterator für Inorder-Besuch
Tiefensuche-Iterator für Inorder-Besuch
ƒ Beim next()-Aufruf wird der Knoten position zurückgegeben.
ƒ Für den nächsten next()-Aufruf wird position fortgeschaltet.
ƒ Fall 1: Der aktuelle Knoten position (der keinen linken
Nachfolger haben kann), hat einen rechten Nachfolger.
Æ Dann wird zu diesem rechten Nachfolger gegangen und von dort
class TreeSetIterator<Type>
implements java.util.Iterator<Type> {
// Zu traversierender Baum
private TreeSet<Type> treeSet;
private Entry position; // Aktuelle Position
public TreeSetIterator(TreeSet<Type> treeSet) {
this.treeSet = treeSet; //ggf. Baum vorher kopieren
// Erste Position ist Knoten "ganz links"
position = treeSet.root;
if (position != null) {
while (position.left != null) {
position = position.left;
} } }
sofort weiter so weit wie möglich nach links unten.
alte
position
neue
position
public boolean hasNext() {
return position != null;
}
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-53
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-54
16.4 Graph-Traversierung
16.4 Graph-Traversierung
Tiefensuche-Iterator für Inorder-Besuch
ƒ Fall 2: Der aktuelle Knoten position (der keinen linken
Nachfolger haben kann), hat auch keinen rechten Nachfolger.
Æ Rückweg Richtung Wurzel
Fall 2a: von links kommend
neue
position
alte
position
Fall 2b: von rechts kommend
neue position
alte
position
public Type next() {
Type result = position.value;
if (position.right != null) {
//Ergebnis konservieren
//Fall 1
position = position.right;
while (position.left != null) position = position.left;
} else { //Fall 2
if (position == treeSet.root) position = null; //Wurzel erreicht
else {
Entry prevPosition = position;
// Schleppzeiger
position = position.parent;
// Aufstieg um eins
while (
position.right==prevPosition
&& position!=treeSet.root){
prevPosition = position; position = position.parent;
}
if (position.right==prevPosition && position==treeSet.root)
//Wurzel erreicht
position = null;
}
Wie unterscheidet man zwischen
den Fällen 2a und 2b?
//Schleppzeiger
Entry previous = position;
position = position.parent;
if (position.right != previous){
//Fall 2a
} else { //Fall 2b
while (...) {...}
}
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-55
© Michael Philippsen
if (position.right != null) {
position = position.right;
while (position.left != null) {
position = position.left;
}
}
}
return result; //konserviertes Ergebnis zurückgeben
}
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-56
16.4 Graph-Traversierung
16.4 Graph-Traversierung
DFS-Anwendungsbeispiel: DFS-Nummerierung/-Baum
DFS-Anwendungsbeispiel: DFS-Nummerierung/-Baum
ƒ Die Tiefensuche durchläuft die Knoten eines Graphen in einer
bestimmten Reihenfolge.
ƒ Wenn jedem Knoten die Position in dieser Reihenfolge zugeordnet
wird, ist das eine DFS-Nummerierung.
ƒ DFS-Nummern sind für viele Graph-Algorithmen nützlich.
Startknoten
Knoten mit DFS-Nummern:
1
2
6
3
7
ƒ Umsetzung mit DFS-Algorithmus:
5
□ Globale Variable dfs = 1 vor dem Start initialisieren
□ präKnotenArbeit(v): v.dfs = dfs++
DFS-Nummer des Knotens wird auf den Wert der globalen Variable
gesetzt; diese wird inkrementiert.
8
4
Durchgezogene Kanten verbinden Knoten mit denjenigen Nachfolgern,
die die DFS-Nummerierung als noch unmarkiert vorgefunden hat.
kantenArbeitif baut aus diesen Kanten den sog. DFS-Baum (oder
Tiefensuchbaum, „DFS-tree“) auf, einen Spannbaum.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-57
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-58
16.4 Graph-Traversierung
16.4 Graph-Traversierung
Wichtige Eigenschaft ungerichteter DFS-Bäume
Tiefensuche in gerichteten Graphen:
ƒ Seien G=(V,E) ein ungerichteter Graph und T=(V,F) ein DFSBaum von G, dann gilt für alle Kanten e0E entweder e0F oder e
verbindet zwei Knoten von G, von denen einer Vorfahre des
anderen in T ist.
Beweis:
ƒ Prinzip ist gleich wie bei ungerichteten Graphen.
ƒ Aber Problem: Welcher Teil des Graphen abgesucht wird, hängt
vom Startknoten ab:
Am Beispiel:
1
1
2
6
3
2
7
7
5
3
6
8
8
4
4
Es gibt keine
Querverbindungen
Es sei (v,u) Kante von G, v sei
bereits besucht. Nach dem
Markieren von v werden die
Nachfolger von v besucht.
Wäre u als erster Nachfolger
besucht worden, dann würde
(v,u) zu F gehören.
Oder u wird besucht, ehe DFS
zum Vorgänger von v zurückkehrt, dann ist u Nachfolger von
v in T.
A
B
C
V
Tiefensuche(V) durchläuft alle Knoten
Tiefensuche(A) durchläuft nur die Knoten A,B und C
Ebenso wie man bei ungerichteten Graphen davon ausgeht, dass
der DFS-Algorithmus so lange läuft, bis alle Zusammenhangskomponenten bearbeitet sind, geht man auch bei gerichteten
Graphen davon aus, dass DFS so oft mit noch unbesuchten
Knoten wiederholt wird, bis alle Knoten besucht worden sind.
5
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-59
© Michael Philippsen
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-60
16.4 Graph-Traversierung
16.4 Graph-Traversierung
Verschiedene Kantenarten in DFS-Bäumen gerichteter
Graphen
Zyklenerkennung
1
ƒ Definiert für DFS-Baum im gerichteten Graphen:
□ Baumkanten sind Teil des Tiefensuchbaums.
Nicht Teil des Tiefensuchbaums sind:
□ Vorwärtskanten verbinden Knoten mit einem Nachkommen.
□ Rückwärtskanten verbinden mit einem Vorfahr.
□ Querkanten verbinden „nicht direkt verwandte“ Knoten.
1
Baumkante („tree edge“)
2
6
3
7
Vorwärtskante („forward edge“)
Rückwärtskante („backward edge“)
4
5
Querkante („cross edge“), von rechts nach links
Rückwärtskante („backward edge“)
2
6
3
7
4
5
ƒ Erkennung des Zyklus per DFS-Algorithmus:
□ Auf dem Pfad von der Wurzel nach unten setzt man eine boolesche
Variable auf true.
□ Man setzt diese Variable auf false, wenn man alle Nachfolger
besucht hat, ohne einen Zyklus zu finden.
□ Eine Rückwärtskante (v,w) erkennt man daran, dass die boolesche
Variable von w noch immer gesetzt ist. (Bei Vorwärtskanten und
Querkanten ist die Variable bereits wieder auf false gesetzt worden.)
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-61
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-62
16.4 Graph-Traversierung
16.4 Graph-Traversierung
Zyklenerkennung
Zyklenerkennung
1
ƒ Erkennung des Zyklus per DFS-Algorithmus:
□ Auf dem Pfad von der Wurzel nach unten setzt man eine boolesche
Variable auf true.
□ Man setzt diese Variable auf false, wenn man alle Nachfolger
besucht hat, ohne einen Zyklus zu finden.
□ Eine Rückwärtskante (v,w) erkennt man daran, dass die boolesche
Variable von w noch immer gesetzt ist. (Bei Vorwärtskanten und
Querkanten ist die Variable bereits wieder auf false gesetzt worden.)
Einen (gerichteten) Zyklus kann es nur
geben, wo es eine Rückwärtskante gibt.
2
3
5
4
Zwei Wege zum
Knoten 4, ab hier
wird der Graph
doppelt traversiert.
ƒ Konkret:
□ präKnotenArbeit(v): v.onPath = true;
□ knotenArbeitimmer(e): if (e.target.onPath) {
foundCycle = true;
break;
Zurücksetzen
}
nachdem letzter
□ postKnotenArbeit(v): v.onPath = false;
Nachfolger
ƒ Optimierung:
□ postKnotenArbeit(v) wird erweitert um: v.done = true;
□ knotenArbeitimmer(e): wird erweitert um:
if (e.target.done) {
break;
}
besucht wurde.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-63
© Michael Philippsen
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-64
16.4 Graph-Traversierung
16.4 Graph-Traversierung
Wichtige Eigenschaft gerichteter DFS-Bäume
Grundidee Breitensuche (BFS, „breadth-first-search“)
1
2
6
3
7
Seien G=(V,E) ein gerichteter Graph und T=(V,F)
ein DFS-Baum von G, dann gilt für alle Kanten
e=(v,w)0E: wenn v.dfs<w.dfs, dann ist v Vorfahre
von w in T.
ƒ Besuche die Knoten eines Graphen „ebenenweise“: also erst alle
Knoten mit Höhe 0, dann alle mit Höhe 1, dann alle mit Höhe 2, …
2
mögliche Breitensuch-Reihenfolgen:
2, 13, 7, 5, 3, 11, 1
2, 7, 13, 11, 3, 5, 1
…
4
5
13
5 ist weder Vorfahre von 4 noch von 2.
7 ist nicht Vorfahre von 5.
1 ist aber Vorfahre von 5.
5
7
3
11
1
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-65
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-66
16.4 Graph-Traversierung
16.4 Graph-Traversierung
Breitensuche am Beispiel:
Iterative BFS-Implementierung mit Schlange
2
X
X
5
1
XX X
4
3
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-67
© Michael Philippsen
void BFS(Graph G, Node v) {
Schlange s = new Schlange();
s.enq(v); //merke Wurzel für Besuch vor
while (!s.isEmpty()) {
//besuche vorderstes Schlangenelement
v = s.front(); s.deq();
v.mark();
//Arbeit(v);
Iterator iter = v.getEdges();
while (iter.hasNext()) {
//lege alle Nachf. in Schlange
Edge e = (Edge) iter.next();
if (e.target.isUnmarked()) {
s.enq(e.target);
}
}
}
}
Implementierung:
ersetze den Keller
der iterativen DFSImplementierung
durch Schlange
(FIFO).
prä/post/in sind
nicht wohldefiniert.
Daher nur arbeit.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-68
16.4 Graph-Traversierung
16.5 Topologische Sortierung
Breitensuche mit Schlange
ƒ Beispiel: Eine Menge von Aufgaben muss erledigt werden.
Manche Aufgaben hängen von einer anderen Aufgabe ab und
können erst begonnen werden, wenn die erste Aufgabe erledigt
ist. In welcher Reihenfolge sollten die Aufgaben hintereinander
ausgeführt werden?
2
13
5
7
3
11
1
2
13 7
753
5 3 11
3 11 1
11 1
1
ƒ Gegeben: gerichteter azyklischer Graph G=(V,E) mit n Knoten.
ƒ Gäbe es einen Zyklus, dann kann keine sequentielle Reihenfolge
der Aufgaben gefunden werden, die alle Abhängigkeiten
berücksichtigt.
ƒ Gesucht: Nummerierung der Knoten von 1 bis |V|=n, sodass alle
Knoten, die von einem Knoten v mit Nummer k aus erreicht
werden können, eine Nummer >k haben.
1
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-69
16.5 Topologische Sortierung
ƒ Hypothese: Es ist klar, wie man Graphen mit <n Knoten
topol. sortiert.
ƒ Induktionsanfang: Der einzige Knoten eines Graphen mit |V|=1
bekommt die Nummer 1; dies ist die topologische Sortierung.
ƒ Induktionsschritt „n-1Æn“:
□ Es gibt sicher (mindestens) einen Knoten v mit Eingangsgrad 0.
(Sonst gäbe es einen Zyklus.)
□ Ein Knoten mit Eingangsgrad 0 bekommt die Nummer 1.
□ Für die übrigen n-1 Knoten wird nach Induktionsvoraussetzung eine
topologische Sortierung von 2…n“ berechnet.
Eingangsgrad 0
1
3
2
4
5
6
7
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-70
16.5 Topologische Sortierung
Algorithmus zum topologischen Sortieren
1. Traversiere den Graphen und berechne für jeden Knoten eine
Instanzvariable indegree, die mit dem Eingangsgrad initialisiert
wird:
kantenArbeit(e): e.target.indegree++
2. Knoten mit indegree==0 werden in einen beliebigen
Behälterdatentyp (Liste, Schlange, Keller, …) gesteckt.
3. Initialisiere einen globalen Zähler topolSort = 0
4. Solange der Behälter nicht leer ist:
Entnimm einen Knoten v und setze v.topoNr=topolSort++;
Für alle direkten Nachfolger w von v
Setze w.indegree -= 1;
Wenn w.indegree==0 dann füge w in den Behälter ein
Aufwand O(|V|+|E|): indegree-Berechnung O(|V|+|E|),
Behälterzugriff O(1), jede Kante wird einmal berücksichtigt, wenn
indegree des Zielknotens verkleinert wird O(|E|).
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-71
© Michael Philippsen
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-72
16.6 Kürzeste Pfade
16.6 Kürzeste Pfade
Suche nach dem kürzesten Pfad
Suche nach einem geschlossenen
Pfad durch gegebene Knoten:
ƒ Gegeben:
ƒ Gesucht:
Wie besucht ein Handlungsreisender alle seine
Kunden mit einer Rundreise?
L
400
Suche nach einem „billigen“ Pfad:
KA
Wie komme ich am billigsten von
Stuttgart nach Leipzig?
L
oder beides:
Billigste Rundreise?
400
KA
S
70
300
300
70
150
N
S
v
150
300
300
Ein Graph, zwei Knoten v und w.
Der (nach Anzahl der Kanten) kürzeste Pfad.
von v nach w.
N
100
M
230
100
M
230
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-73
w
ƒ Verallgemeinerung:
Pfadlänge nicht durch Kantenzahl bestimmt, sondern durch Summe
der Kantengewichte (die alle positiv sein sollen).
ƒ Beispiel:
□ Gegeben: Eine Straßenkarte mit Entfernungsangaben
zwischen Kreuzungen.
□ Aufgabe: Finde die kürzeste Route von Erlangen nach Berlin.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-74
16.6 Kürzeste Pfade
16.6 Kürzeste Pfade
Bemerkungen
Grundidee für kürzeste Pfade
ƒ In gerichteten Graphen gilt natürlich nicht, dass der kürzeste Pfad
von v nach w der gleiche ist wie der von w nach v.
ƒ Angenommen man kennt die kürzesten Pfade („shortest path“,
sp) zu allen direkten Vorgängern eines Knoten w.
2
24
3
8
13
X
V
ƒ Für jeden kürzesten Pfad p = (v0, v1, ..., vk) von v0 nach vk ist jeder
Teilpfad p‘ = (vi, ..., vj), 0≤i<j ≤k, ein kürzester Pfad von vi nach vj.
□ Widerspruchsbeweis: Angenommen es gäbe einen kürzeren Pfad p“
von vi nach vj. Dann kann in p p‘ durch p“ ersetzt werden, der
entstehende Pfad von v0 nach vk wäre kürzer als p.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-75
© Michael Philippsen
11
5
17
19
Y
Z
22
W
6
25
c(sp(v,x))=13
c(sp(v,y))=17
c(sp(v,z))=19
Zur Erinnerung:
c(w) = Kosten
des Pfads w.
ƒ Dann ist c(sp(v,w)) = min( c(sp(v,x)) + c(x,w),
c(sp(v,y)) + c(y,w),
c(sp(v,z)) + c(z,w)) = min(24,22,25)=22
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-76
16.6 Kürzeste Pfade
16.6 Kürzeste Pfade
Algorithmus von Dijkstra
ƒ
ƒ
Algorithmus von Dijkstra
Induktionsbeginn:
ƒ
Induktionsschritt „kÆk+1“:
□ Suche denjenigen Knoten wóVk, der den k+1-kleinsten Abstand zu v hat.
□ Der kürzeste Pfad von v nach w kann nur durch Vk laufen, nur die letzte
Kante kann einen Knoten u0Vk mit w verbinden.
(Sonst wären andere Knoten in Vk.)
□ Dann ist (siehe „Grundidee“) der Knoten w mit minimalen Kosten
c(sp(v,w)) = minu0Vk ( c(sp(v,u)) + c(u,w) ) der gesuchte k+1-te Knoten.
□ Betrachte Knoten v und alle seine abgehenden Kanten.
□ Offensichtlich: Wenn (v,w) die kürzeste Kante ist, die von v abgeht, dann
ist w der Knoten, der am nächsten an v liegt.
V1={w}
der Knoten mit dem
v
x
w
2
1
kürzesten Abstand zu v
V2={w,x}
die 2 Knoten …
3
y
V3={w,x,y}
die 3 Knoten …
Induktionsannahme:
□ Die Menge Vk der k Knoten, die am nächsten an v liegen, ist bekannt.
□ In Vk ist also der Knoten mit dem kürzesten Abstand zu v, mit dem
zweitkleinsten Abstand zu v, …, mit dem k-kleinsten Abstand zu v.
ƒ
v
w
Einer dieser Knoten ist
der gesuchte k+1-te
Knoten
Induktionsschritt „kÆk+1“:
□ Suche denjenigen Knoten wóVk, der den k+1-kleinsten Abstand zu v hat.
Problem: Aufwändige Berechnung der Kosten für alle wóVk
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-77
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-78
16.6 Kürzeste Pfade
16.6 Kürzeste Pfade
Algorithmus von Dijkstra
ƒ
Vk
Algorithmus von Dijkstra
Effizienzverbesserung
ƒ
v
Die Kosten des
Pfades von v zu
diesem Knoten
seien c.
Vk
Sei w der Knoten, mit
dem k+1-kleinsten
Abstand zu v.
z
v
Die Kosten des
Pfades von v zu
diesem Knoten
bleiben unverändert!
w
Vk+1
z
w
w
Wegen der gestrichelten
Kante könnte es in Vk+1
einen billigeren Weg zu z
geben als vorher in Vk.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-79
© Michael Philippsen
Effizienzverbesserung
□ Speichere die Kosten der kürzesten Wege zu Knoten außerhalb
von Vk.
□ Diese Kosten können meistens unverändert als Kosten der
kürzesten Wege zu Knoten außerhalb von Vk+1 übernommen
werden.
□ Nur die Pfade zu w‘óVk+1, die über den neuen Knoten w (wóVk,
aber w0Vk+1) führen, könnten sich verbilligen (oben: gestrichelte
Kante).
v
z
gespeicherter min.
Pfad zu z
+
?
<
wenn ja: gespeicherten
min. Pfad zu z aktualisieren
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-80
16.6 Kürzeste Pfade
16.6 Kürzeste Pfade
Algorithmus von Dijkstra auf einen Blick
Algorithmus von Dijkstra am Beispiel
ƒ
ƒ
Markiere alle Knoten als unbesucht.
Setze v.sp=0 und w.sp=4 für alle w≠v
ƒ
Solange nicht besuchte Knoten existieren
1. Minimumsauswahl:
wähle/besuche einen Knoten w, mit w.sp minimal
(1. Iteration: nur v.sp=0, daher wird v gewählt)
2. Aktualisierung:
Für alle Kanten (w,z) zu unbesuchten z:
Wenn (w.sp + c(w,z) < z.sp)
dann setze z.sp := w.sp + c(w,z)
(1. Iteration: Kosten der direkten Nachfolger von v werden gesetzt.)
V
5
A
3
C
V
V
B
5
12
A
8
2 1
4
3
C
D
bisher
gefundene
billigste
Wege
B
12
A
8
2 1
4
V
0
D
5
3
C
V
B
12
A
8
2 1
4
A
B
4
4
5 min- 12
Wahl
10min-
D
C
4
4
8 minWahl
Wahl
5
3
C
V
B
5
12
A
8
2 1
4
D
D
4
4
13
12
11min-
3
C
B
12
8
2 1
4
Gestrichelte
Kanten
bilden den
Spannbaum
kürzester
Pfade.
Wahl
besucht
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-81
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-82
16.6 Kürzeste Pfade
16.6 Kürzeste Pfade
Aufwand von Dijkstras Algorithmus
Aufwand von Dijkstras Algorithmus
ƒ
Minimumsauswahl:
Die Halde ist die ideale Datenstruktur für die Suche nach dem
Knoten w mit minimalem Pfad. O(1) für Zugriff auf das Minimum,
O(log2|V|) für das Entfernen von w aus der Halde.
Æ Insgesamt sind |V| Knoten zu suchen & entfernen:
O(|V|·log2|V|)
ƒ
Aktualisierung:
Für jeden Knoten w müssen dessen Nachfolger z gefunden und
ggf. die Pfade zu z korrigiert werden.
Die Halde unterstützt die Suche nach z nicht. Daher ist eine
Reihung erforderlich, die für jedes z dessen Position in der Halde
angibt.
□ Die Korrektur der Pfadlänge zu z macht eine Umsortierung der Halde
erforderlich O(log2|V|).
□ Insgesamt führen |E| Kanten zu solchen Aktualisierungen:
O(|E|·log2|V|).
Æ Gesamtaufwand: O((|E|+|V|) ·log2|V|)
Mit sog. Fibonacci-Halden geht es noch besser:
O(|E|+|V|·log2|V|), das sprengt aber den Rahmen der
Algorithmik-1.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-83
© Michael Philippsen
D
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-84
16.7 Minimaler Spannbaum
16.7 Minimaler Spannbaum
Beispiel-Problem:
Aufgabe:
ƒ
ƒ
ƒ
Gegeben: Eine Landkarte als Graph G.
Gesucht: Es soll ein Wasserleitungsnetz mit minimalen Kosten
aufgebaut werden, das alle Ortschaften versorgt. Die
Gesamtkosten des Netzes sind proportional zur Summe der
Längen aller vorkommenden Leitungen.
Ähnliche Probleme gibt es beim Entwurf von Rechnernetzen.
Was ist besser?
ƒ
ƒ
Gegeben: Ein ungerichteter Graph mit Kantengewichten.
Gesucht: Zusammenhängender Teilgraph der alle Knoten
enthält, wobei die Summe der Kantengewichte minimal ist.
Eigenschaft:
ƒ
Der gesuchte Teilgraph ist azyklisch:
□ Denn gäbe es einen Zyklus, dann könnte man zumindest eine Kante
entfernen (also Kosten verringern) und trotzdem alle Knoten
erreichen.
ƒ
ƒ
Also ist der gesuchte Teilgraph ein Baum.
Genauer, ein Spannbaum (aufspannender Baum)
oder
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-85
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-86
16.7 Minimaler Spannbaum
16.7 Minimaler Spannbaum
Erster Ansatz:
Zweiter Ansatz: Algorithmus von Prim
ƒ
Der Algorithmus für die Suche nach dem kürzesten Pfad lieferte
für einen gegebenen Knoten einen Spannbaum kürzester Pfade.
□ In jedem Schritt wurde eine Kante so hinzugefügt, dass der
entstehende Pfad minimale Länge hatte.
□ Aufwand: O(|E| + |V|·log|V|).
ƒ
Man kann den minimalen Spannbaum finden, indem man den
Spannbaum kürzester Pfade für jeden Knoten berechnet und
dann den kleinsten dieser Spannbäume auswählt.
ƒ Angenommen man hat einen Teilgraph T von G gefunden, der
auch Teilgraph des minimalen Spannbaums ist.
ƒ Wie kann man T um eine weitere Kante vergrößern?
zu T gehörig
T
□ O(|V|·(|E| + |V|·log|V|)).
Es gibt Knoten, die über eine Kante mit T verbunden sind,
die aber noch nicht zu T gehören.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-87
© Michael Philippsen
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-88
16.7 Minimaler Spannbaum
16.7 Minimaler Spannbaum
Algorithmus von Prim am Beispiel
Algorithmus von Prim
ƒ Die Kante mit dem kleinsten Gewicht, die einen zu T benachbarten
Knoten verbindet, wird zum minimalen Spannbaum hinzugenommen.
zu T gehörig
T
A
D
w
B
1
2 6
12
4
C
11 9
F
19 1317 22 28
G
I
H
u
Zu untersuchende Kanten, sortiert:
(BA)-1
Mit Knoten B kommen
(ED)-6
neue Kanten hinzu,
(EF)-11
einsortieren.
(BC)-12
(EH)-17
ƒ (u,w) sei die billigste Kante, die einen Knoten von T mit einem Knoten
außerhalb von T verbindet.
ƒ (u,w) gehört zum minimalen Spannbaum. Andernfalls gäbe es einen Pfad
von irgendeinem Knoten aus T zu w mit niedrigeren Kosten. Da jeder
Pfad T verlassen muss, hat dieser Pfad mindestens die Kosten der
ersten Kante, die T verlässt. Diese sind aber höher als die Kosten der
Kante (u,w). Einen kleineren Spannbaum bekäme man also, indem man
diese erste Kante durch (u,w) ersetzt.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-89
16.7 Minimaler Spannbaum
1
B
12
C
2 6 4 11 9
F
19 1317 22 28
D
G
H
I
Knoten A kommt hinzu.
Zu untersuchende Kanten, sortiert:
(AD)-2
Wegen Hinzunahme
(ED)-6
von D wird diese
(EF)-11
Kante gestrichen.
(BC)-12
(EH)-17
Knoten D kommt hinzu.
Zu untersuchende Kanten, sortiert:
(EF)-11
(BC)-12
(EH)-17
(DG)-19
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-91
© Michael Philippsen
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-90
16.7 Minimaler Spannbaum
Algorithmus von Prim am Beispiel
A
Starten wir zum Beispiel mit Knoten E.
E gehört sicher zum Spannbaum.
Zu untersuchende Kanten, sortiert:
(EB)-4
(ED)-6
Minimum
(EF)-11
(EH)-17
Algorithmus von Prim am Beispiel
A
1
B
12
C
2 6 4 11 9
F
19 1317 22 28
D
G
H
I
Knoten F kommt hinzu.
Zu untersuchende Kanten, sortiert:
(FC)-9
Wegen Hinzunahme
(BC)-12
von C wird diese
(EH)-17
Kante gestrichen.
(DG)-19
(FI)-28
Knoten C kommt hinzu.
Zu untersuchende Kanten, sortiert:
(EH)-17
(DG)-19
(FI)-28
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-92
16.7 Minimaler Spannbaum
16.7 Minimaler Spannbaum
Algorithmus von Prim am Beispiel
A
D
1
2 6
B
4
12
C
11 9
F
19 1317 22 28
G
H
I
Knoten H kommt hinzu.
Zu untersuchende Kanten, sortiert:
(HG)-13
(DG)-19
(HI)-22
(FI)-28
Knoten G kommt hinzu.
Zu untersuchende Kanten, sortiert:
(HI)-22
(FI)-28
ƒ Beachte die Analogie zu Dijkstras Algorithmus:
□ Minimumauswahl unter den Kanten, die aus T herausführen.
□ T vergrößern.
□ Aktualisierung und gleiche Idee zur Effizienzverbesserung:
Statt alle Kanten, die aus dem neuen T herausführen, neu zu
untersuchen, aktualisiert man die Kosten der billigsten Kante aus T
für alle Nachbarknoten von w.
ƒ Durch diese Änderung berechnet derselbe Algorithmus den
minimalen Spannbaum.
ƒ Natürlich mit dem gleichen Gesamtaufwand: O(|E|+|V|·log2|V|)
Knoten I kommt hinzu.
Keine Kanten mehr zu untersuchen.
Minimaler Spannbaum gefunden.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-93
16.7 Minimaler Spannbaum
Dritter Ansatz: Algorithmus von Kruskal
ƒ Statt einen Teilgraphen des Minimalen Spannbaums in jedem
Schritt um eine Kante zu erweitern, beginnt man mit einem Wald
von einzelnen Knoten und fügt diese nach und nach zum
Minimalen Spannbaum zusammen.
ƒ Beginne mit sortierter Kantenliste in aufsteigender Reihenfolge.
ƒ Kante gehört zur Lösung, wenn sie
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-94
16.7 Minimaler Spannbaum
Algorithmus von Kruskal am Beispiel
1
12
2 6 4 11 9
19 1317 22 28
Sortierte Kantenliste (hier nur Gewichte):
1 2 4 6 9 11 12 13 17 19 22 28
entfällt, da Zyklus
entstünde
dito
dito
□ einen vorhandenen Baum um einen noch nicht betrachteten Knoten
erweitert,
□ zwei noch nicht betrachtete Knoten verbindet (zu einem neuen
Baum),
□ zwei verschiedene Bäume verbindet.
ƒ Jetzt kann man die Kanten nacheinander betrachten und sofort
entscheiden, ob die Kante zur Lösung gehört oder nicht.
□ Es ist garantiert, dass kein Zyklus entsteht.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-95
© Michael Philippsen
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-96
dito
16.7 Minimaler Spannbaum
16.7 Minimaler Spannbaum
Andere Sichtweise:
Daraus dann Beweis für Korrektheit:
ƒ
ƒ
ƒ
Induktion: Zu jedem Zeitpunkt haben wir einen Wald minimal
spannender Bäume.
Induktionsanfang: Jeder Knoten ist für sich ohne Kanten ein
minimal spannender Baum.
Induktionsschritt: Vereinige durch Hinzufügen zwei Bäume zu
einem, sodass dieser seine Knoten auch minimal aufspannt.
der Graph G = (V,E) habe n Knoten
sortiere E nach aufsteigender Länge
Erzeuge zu jedem Knoten seinen minimalen Spannbaum (Induktionsanfang)
für i von 1 bis n-1 (Induktionsschritt)
füge die kürzeste noch nicht hinzugefügte Kante zum
Wald hinzu, sofern dies keinen Zyklus entstehen lässt
ƒ
ƒ
Induktionsanfang: trivial
Induktionsschritt:
□ Nach Induktion gilt, dass der Teilgraph G ein minimaler Spannbaum
ist und ebenso Teilgraph H.
□ Nach Verfahren ist e die kürzeste Kante, die G und H verbindet.
□ G∪H∪e ist auch minimal, weil G∪H∪f nicht besser sein kann, und
eine „Umordnung“ G oder H „länger“ macht.
□ Würde man G und H mit mehr als einer Kante verbinden, würde ein
Zyklus entstehen, außer man entfernt dafür andere (kürzere) Kanten
⇒ Summe wieder länger.
e
G
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-97
H
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-98
16.7 Minimaler Spannbaum
16.7 Minimaler Spannbaum
Aufwand von Kruskals Algorithmus
Aufwand von Kruskals Algorithmus
ƒ Jede Kante wird aus der Halde entfernt: O(|E|·log2|E|).
ƒ Aufwand für Baumerweiterung und Test auf Zyklenfreiheit?
v1
v2
Verweis auf Identifikator der Zusammenhangskomponente.
Anfangs eine Komponente für jeden Knoten.
v3
v5
Aufwand des Zyklentests: Beim Einfügen von (vi,vj) wird
überprüft, ob vi und vj in derselben Zusammenhangskomponente liegen. Dann Zyklus. Aufwand des Tests: O(1)
…
…
Baumerweiterung: Konkatenation und Zeigerkorrektur
v4
Vn-1
x
vk1 vk2 vk3
vn
y
vl1 vl2 vl3 … vly
…
vkx
Zusammenhangskomponente mit x Knoten,
nämlich vk1, …, vkx
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-99
© Michael Philippsen
Aufwand für Baumerweiterung?
Wenn bei der Baumerweiterung eines Knotens der
v1
Zeiger kopiert wird, dann war der Knoten in der
v2
kleineren Gruppe. Das kann insgesamt höchstens
v3
log2|V| mal der Fall sein, weil dann der Spannbaum
v4
erreicht ist. Da jede Kante einmal betrachtet wird,
v5
ergibt sich als Gesamtkosten der Baumerweiterung
…
…
Vn-1
vn
O(|E|·log2|V|).
x+y vk1 vk2 vk3
…
vkx vl1 vl2 vl3 … vly
kürzere Liste wird angehängt, O(1)
gestrichelt: y Zeiger werden korrigiert.
Æ Gesamtaufwand O(|E|·log2|E|)
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-100
16.8 Transitive Hülle
16.8 Transitive Hülle
Gegeben sei ein gerichteter Graph G=(V,E). Die transitive
Hülle C=(V,F) von G ist ein gerichteter Graph, in dem es genau
dann eine Kante (v,w)0VxV gibt, wenn es in G einen gerichteten
Pfad vÆ*w gibt.
G:
Transitive Hülle von G:
Lösung durch Reduktion auf ein anderes Problem
ƒ Die transitive Hülle kann man finden, indem man das
Problem
auf ein anderes (bekanntes) Problem reduziert.
ƒ Sei G‘=(V,E‘) ein vollständig verbundener Graph („jeder mit
jedem“). Jede Kante e0E‘ hat das Gewicht 0, wenn sie auch
Kante in G ist (e0E) und 1 sonst.
ƒ Es gibt also einen Pfad von v nach w in G, wenn der
kürzeste Pfad zwischen v und w in G‘ die Länge 0 hat.
Æ Berechne die Länge aller kürzesten Pfade für jeden Knoten
aus G‘; dann kann die Transitive Hülle abgelesen werde.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-101
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-102
16.8 Transitive Hülle
16.9 Matching
Warshall‘s Algorithmus
Elegante Verwendung der Adjazenzmatrix
Bipartiter Graph:
boolean [][] A = {...}
int dim = A.length;
...
void warshall() {
for (int y = 0; y < dim; y++) {
for (int x = 0; x < dim; x++) {
if (A[x][y]) {
for (int z = 0; z < dim; z++) {
if (A[y][z])
A[x][z] = true;
}
}
}
}
}
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-103
© Michael Philippsen
ƒ
ƒ
ƒ
Ungerichteter Graph G=(U ∪V,E) mit U∩V = ∅ und nur Kanten
[v1, v2] ∈ E mit v1∈U, v2∈V.
In diesem Abschnitt betrachten wir nur ungerichtete Graphen.
Praktische Relevanz: Viele Zuordnungsprobleme ordnen Dinge
verschiedener Arten einander zu, z.B.
□
□
□
□
ƒ
Männer und Frauen im Tanzkurs
Arbeiten und Arbeitskräfte
Koffer und Schließfächer
usw.
Bäume sind bipartit (ebenenweise aufteilen).
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-104
16.9 Matching
16.9 Matching
Beispiel
ƒ Gegeben: Wir befinden uns im Tanzkurs.
Jeder Teilnehmer (Knoten) weiß,
mit wem er gerne tanzt (Kante).
ƒ Aufgabe: Bestimme mögliche Paarungen.
Heino
Klaus
Martin
Maria
Eva
Heino
Eva
Pia
Uwe
Lilo
Martin
Klaus
Maria
Pia
Uwe
Lilo
ƒ Drei Paare sind gefunden (gestrichelt), aber nicht jeder
Knoten hat einen Partner, und es sind keine weiteren
Paarungen möglich.
ƒ Frage: Wie kriegt man eine optimale Paarbildung zustande?
□ Es ist ja noch ein Herr und eine Dame übrig geblieben!
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-105
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-106
16.9 Matching
16.9 Matching
Anderes Beispiel
ƒ Jeder Algorithmik-Student habe 2 Wünsche für
Übungsgruppen.
Die Übungsgruppen haben eine begrenzte Zahl an Plätzen.
ƒ Gibt es eine Lösung, die alle Übungsgruppenwünsche erfüllt?
Definitionen
ƒ Zwei Kanten (u,v) und (x,y) heißen unabhängig, wenn
u,v,x,y vier verschiedene Knoten sind.
Wenn u=x oder u=y oder v=x oder v=y, dann heißen die
Kanten benachbart (oder verbunden oder adjazent).
ƒ Eine Kantenmenge M heißt unabhängig, wenn alle ihre
Elemente paarweise unabhängig sind. Solche
Kantenmengen heißen auch Matching.
Studenten
Tutorien
…
…
…
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-107
© Michael Philippsen
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-108
16.9 Matching
16.9 Matching
Definitionen
Bemerkungen
ƒ Ein Zyklus mit gerader Knotenzahl hat genau 2 perfekte
Matchings.
ƒ
ƒ
ƒ
ƒ
ƒ
Ein Knoten heißt frei bezüglich eines Matchings,
wenn er keine Kante des Matchings hat, sonst
sagt man, dass er zum Matching gehört.
Ein Matching heißt perfekt, wenn es alle Knoten
des Graphen überdeckt.
Ein Matching heißt größtes Matching, wenn es
um keine Kante erweitert werden kann.
Ein Matching heißt größtmögliches Matching,
wenn es kein Matching mit mehr Kanten gibt.
Ein Matching bei dem nur ein Knoten frei bleibt,
heißt fast perfekt.
„ge-match-t“
frei
ƒ Nicht jeder Graph hat ein (fast) perfektes Matching.
ist nicht perfekt
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-109
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-110
16.9 Matching
16.9 Matching
Ein gieriger Algorithmus:
Solange eine unmarkierte Kante (u,v) in G existiert
ƒ Gegeben: Graph G
markiere (u,v)
markiere alle zu u oder v inzidenten Kanten
ƒ Gesucht: Matching M
Idee: Verbesserung eines größten Matchings
übernimm (u,v) nach M
Um diesen freien
Knoten ins Matching
aufzunehmen…
…muss diese MatchingKante ersetzt werden.
Dieser gierige Algorithmus liefert
□ ein größtes Matching,
□ aber im Allg. weder größtmögliches noch (fast) perfektes Matching.
ƒ Verbesserung der Matchingqualität durch Heuristiken:
□ zuerst Knoten mit geringem Grad bearbeiten („schwer zu verkuppeln“)
□ …
ƒ
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-111
© Michael Philippsen
Idee klappt
bei alternierenden
Pfaden:
}A
}B
}A
}B
ersetze hier 2 Kanten des
Matchings durch 3 neue Kanten
}A
}B
größtmögliches
(auch perfektes)
Matching
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-112
16.9 Matching
16.9 Matching
Definition
ƒ Ein Pfad heißt (vergrößernd) alternierender Pfad bezüglich
eines Matchings M gdw.
Beispiel
□ der Pfad abwechselnd Kanten 0M und óM hat und
□ Anfangs- und Endknoten des Pfades nicht in M liegen.
v
v
M (fette Linien) ist kein
größtmögliches Matching, da
es einen alternierenden Pfad Verbessern
⇒
zwischen Knoten v,wóM gibt.
w
w
w
M (fette Linien) ist kein
v größtmögliches Matching, da
es einen alternierenden Pfad Verbessern
⇒
zwischen Knoten v,wóM gibt.
Satz von Berge:
Ein Matching M in einem Graphen G ist größtmöglich gdw.
G keinen alternierenden Pfad bezüglich M enthält.
w
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-113
v
v
w
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-114
16.9 Matching
16.9 Matching
Beispiel (2)
Matching-Algorithmus:
Benutze irgendeinen einfachen Algorithmus, um ein größtes Matching M zu finden.
Solange ein alternierender Pfad bezüglich M vorhanden ist,
vertausche die M-Zugehörigkeit aller Kanten auf diesem Pfad.
v
M (fette Linien) ist kein
größtmögliches Matching, da
es einen alternierenden Pfad Verbessern
⇒
zwischen Knoten v,wóM gibt.
w
v
w
In jedem Fall landet man bei einem größtmöglichen Matching.
ƒ Der Algorithmus fügt in jedem Schritt eine Kante zu M hinzu.
ƒ Da es nur endlich viele Kanten gibt, terminiert er.
ƒ Wenn er terminiert, hat er ein größtmögliches Matching
gefunden.
ƒ Noch offen: Wie findet man M-alternierende Pfade?
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-115
© Michael Philippsen
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-116
16.9 Matching
16.9 Matching
ƒ Traversiere den Graphen (egal ob Tiefen- oder
Breitensuche).
ƒ Das Finden eines alternierenden Pfades ist in bipartiten
Graphen G mit zwei Klassen A und B leicht.
□ Jeder Pfad wechselt zwischen M´ und G´\M´.
□ Ein alternierender Pfad ist gefunden, sobald man von einem
Knoten vóM‘ einen Knoten wóM‘ erreicht.
ƒ Sei ein Matching gegeben:
}A
}B
ƒ Vergrößerung der Matchings:
ƒ Definiere G‘ sodass alle Matching-Kanten von A nach B
und alle anderen von B nach A gerichtet sind.
}A
}B
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-117
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-118
16.10 Kantenfärbung
16.10 Kantenfärbung
Definition
ƒ Es sei Graph G=(U ∪V,E) (d.h. U∩V = ∅ und nur Kanten [v , v ] ∈ E mit v ∈U, v ∈V)
ein bipartiter Graph. Eine Kantenfärbung von G mit n Farben
ist eine Abbildung
f:E 6 Fn = {1, 2, 3, …, n}
1
Beispiel
ƒ Es seien U Menge von Lehrern,
V Menge von Schulklassen,
E die Menge der Unterrichtsstunden (Mathematik,
Deutsch, …). Die Farbe gibt
die Zeit des Unterrichts an.
Mo 8-9 Uhr
Mo 9-10 Uhr
2
1
Unterrichtsstunde
Schulklassen
(1a,2a,3a,
3b,…)
…
…
…
Lehrer
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-119
© Michael Philippsen
2
UnterrichtsBeispiel
stunde
ƒ Es seien U Menge von Lehrern,
Schulklassen
V Menge von Schulklassen,
(1a,2a,3a,
3b,…)
E die Menge der Unterrichtsstunden (Mathematik,
…
Deutsch, …). Die Farbe gibt
…
die Zeit des Unterrichts an.
…
Mo 8-9 Uhr
Lehrer
Mo 9-10 Uhr
Problem:
ƒ Wie viele Unterrichtszeiten (Farben) benötigt man und
ƒ wie ordnet man die Unterrichtszeiten zu (wie findet man f)?
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-120
16.10 Kantenfärbung
16.10 Kantenfärbung
Definition
ƒ Es sei Graph G=(U ∪V,E) (d.h. U∩V = ∅ und nur Kanten [v , v ] ∈ E mit v ∈U, v ∈V)
ein bipartiter Graph. Eine Kantenfärbung von G mit n Farben
ist eine Abbildung
f:E6 Fn = {1, 2, 3,… , n}
so dass
1
2
1
2
f ( e1 ) ≠ f ( e2 )
falls e1 und e2 einen gemeinsamen Knoten haben.
Satz
ƒ Es sei Δ(G) der maximale Grad der Knoten eines bipartiten
Graphen G. Dann sind die Kanten des Graphen G mit Δ(G)
Farben färbbar.
Algorithmus zur Kantenfärbung
ƒ Es sei Graph G=(U ∪V,E) ein bipartiter Graph.
ƒ Induktive Vorgehensweise. Sei k die Anzahl der Kanten.
ƒ k=0: Dann braucht man keine Farbe.
ƒ k nach k+1: Es sei e eine Kante mit Ecken (x,y).
Nach Induktionsvoraussetzung ist der Graph mit
G´ = (V,E´), E´=E \ { e }
mit n = Δ(G´) # Δ(G) Farben färbbar. Die Knoten x, y besitzen
in G´ weniger als Δ(G) Farben. Sei
□ α eine Farbe, die keine an x anliegende Kante hat.
□ β eine Farbe, die keine an y anliegende Kante hat.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-121
16.10 Kantenfärbung
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-122
16.10 Kantenfärbung
1. Fall: α = β
ƒ Dann wähle für die neue Kante e diese Farbe α = β.
2. Fall: α … β
ƒ Dann betrachte einen maximalen Pfad mit Anfangspunkt x und
abwechselnd die Farben β und α hat.
Wechsel die Farben α, β in diesem Pfad und färbe e mit β.
Lehrer
x
Schulklassen
e
Lehrer
x
Schulklassen
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-123
© Michael Philippsen
…
y
Farbwechsel
…
…
…
Schulklassen
e
y
y
…
Lehrer
x
e
…
…
…
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-124
…
16.10 Kantenfärbung
Implementierung der Kantenfärbung
ƒ Alle Knoten, Kanten und Farben der Kanten werden in Listen
gespeichert.
ƒ Iteration über die Kanten. In jedem Schritt wird eine weitere
Kante gefärbt. Hierfür sind maximal so viele Operationen
nötig, wie schon Kanten gefärbt wurden. Also
O( 1+2+…+|E|) = O(|E|2)
16.11 Eckenfärbung
Definition
ƒ Es sei Graph G=(U,E) ein Graph. Eine Eckenfärbung von G
mit n Farben ist eine Abbildung
f:U 6 Fn = {1, 2,3, …, n} mit f (e1 ) ≠ f (e2 ) falls [e1,e2] Kante.
Deutschland
Beispiel: Färbung einer Landkarte
Deutschland
h
zugehöriger Grap
Tschechien
Slowakei
Rechenoperationen.
Tschechien
Slowakei
Österreich
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-125
16.11 Eckenfärbung
Satz
ƒ Es sei Δ(G) der maximale Grad der Knoten eines Graphen G.
Dann sind die Ecken des Graphen G mit Δ(G)+1 Farben färbbar.
Ein gieriger Algorithmus (Greedy-Algorithmus)
Österreich
Ungarn
Ungarn
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-126
16.11 Eckenfärbung
Definition:
Ein ebener Graph ist ein Graph, bei dem die Knoten Punkte in
der Ebene sind und alle Kanten sich als Steckenzüge so
darstellen lassen, dass zwei Steckenzüge sich nicht
überschneiden.
ƒ Färbe einen Knoten nach dem anderen wie folgt:
□ Sei P ein nicht gefärbter Knoten.
□ Da es Δ(G)+1 Farben gibt, muss eine Farbe α geben,
die kein Nachbarknoten von P hat.
□ Färbe P mit dieser Farbe.
Implementierung:
ƒ Adjazenzlisten für Nachbarknoten, Reihung für Farbe der Knoten.
ƒ Aufwand: O(|U| * |Δ(G)| ) Rechenoperationen.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-127
© Michael Philippsen
Beispiel:
Graph mit
ƒ 7 Kanten
ƒ 5 Knoten
Hilfssatz (Knotengrad von ebene Graphen):
Ein ebener Graph besitzt einen Knoten, dessen Grad maximal 5 ist.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-128
16.11 Eckenfärbung
16.11 Eckenfärbung
Satz
Ein ebener Graph G ist mit 5 Farben färbbar.
Sehr schwierig ist es zu zeigen,
dass sogar 4 Farben reichen.
3 Farben reichen aber nicht!
P
Algorithmus für 6 Farben:
Algorithmus für 5 Farben:
Sei U Menge der Knoten.
Rekursiver Algorithmus:
ƒ Suche einen Knoten P mit Grad kleiner gleich 5.
ƒ Färbe den induzierten Graphen mit Knotenmenge U \ { P }
ƒ 1. Fall: Die Nachbarknoten von P haben 4 verschiedene
Farben. Dann wähle für P die 5. Farbe.
ƒ Sei U Menge der Knoten.
ƒ Rekursiver Algorithmus:
□ Suche einen Knoten P mit Grad kleiner gleich 5.
□ Färbe den induzierten Graphen mit Knotenmenge U \ { P } .
□ Färbe P mit einer Farbe, die alle seine maximal 5
Nachbarknoten nicht hat. Da es 6 Farben gibt, ist dies möglich.
P
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-129
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-130
16.11 Eckenfärbung
16.11 Eckenfärbung
ƒ
2. Fall: Die Nachbarknoten von P haben 5 verschiedene
Farben. Diese seien wie folgt angeordnet und gefärbt:
ƒ
2.1. Fall: H enthält den blauen Nachbarn von P nicht.
P
H
P
Farbta
u
sch
P
H
Es sein nun H der maximale zusammenhängende Untergraph
der den grünen Nachbarkoten von P enthält und nur grüne
und blaue
Knoten enthält.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-131
© Michael Philippsen
Dann:
□
□
vertausche die Farben grün und blau in H und
färbe P grün.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-132
16.11 Eckenfärbung
ƒ
16.11 Eckenfärbung
2.2. Fall: H enthält den blauen Nachbarknoten von P.
H
Z
P
Farbta
u
sch
P
Betrachte den maximal zusammenhängenden Graph Z, der den hellblauen Nachbarn von P enthält und nur aus roten und hellblauen
Knoten besteht. Aufgrund der Eigenschaften von H, kann Z den
roten Nachbarn von P nicht enthalten.
Tausche daher die Farben rot und hellblau in Z und färbe P hellblau.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-133
16.12 Zusatz: Theorie ebener Graphen
Definition:
Ein ebener Graph ist ein Graph, bei dem die Knoten Punkte in
der Ebene sind und alle Kanten sich als Steckenzüge so
darstellen lassen, dass zwei Steckenzüge sich nicht
überschneiden.
Beispiel:
Graph mit
Implementierung:
ƒ Adjazenzlisten für Nachbarknoten, Reihung für Farben der
Knoten, Halde für Knoten mit minimalem Grad.
ƒ Aufwand:
□ Algorithmus 6 Farben:
O(|U| * log2|U|) Rechenoperationen.
□ Algorithmus 5 Farben:
O(|U|2) Rechenoperationen,
da rekursiv die Zusammenhangskomponenten in jedem
Schritt untersucht und eventuell farblich verändert werden
müssen.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-134
16.12 Zusatz: Theorie ebener Graphen
Eulersche Polyedersatz
Es sei G ein zusammenhängender ebener Graph und
ƒ e die Anzahl der Knoten (Ecken),
ƒ f die Anzahl der Flächen und
ƒ k die Anzahl der Kanten.
Dann gilt:
e–k+f=2
ƒ 7 Kanten
ƒ 5 Knoten
Hilfssatz (Knotengrad von ebene Graphen):
Ein ebener Graph besitzt einen Knoten, dessen Grad maximal 5 ist.
Beweis: Beweis durch Induktion nach der Anzahl der Kanten.
Unterscheide dabei, ob G ein Baum ist oder nicht.
Wie beweist man diesen Satz?
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-135
© Michael Philippsen
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-136
16.12 Zusatz: Theorie ebener Graphen
Folgerung:
Jeder zusammenhängender ebener Graph hat höchstens
3e – 6
Kanten.
Beweis:
Jede Fläche besitzt mindestens 3 Kanten und an jeder Kante
liegen 2 Flächen. Daher gilt:
3f # 2k
Wegen e – k + f = 2 folgt daher k # 3e – 6.
16.12 Zusatz: Theorie ebener Graphen
Hilfssatz (Knotengrad von ebene Graphen)
Ein ebener Graph mit Eckenmenge V besitzt einen Knoten,
dessen Grad maximal 5 ist.
Beweis:
Definiere den Durchschnittsgrad eines Graphen G:
d (G ) := ∑
v∈V
grad G ( v )
|V |
wobei grad G (v ) der Grad des Knoten v ist.
Wegen k # 3e – 6 folgt daher:
d (G ) := ∑
v∈V
grad G ( v ) 2k 2(3e − 6)
=
≤
<6
V
e
|V |
Dies zeigt, dass es einen Knoten vom Grad maximal 5 geben muss.
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-137
© Michael Philippsen
Algorithmen und Datenstrukturen • Philippsen/Stamminger/Pflaum • WS 2008/09 • Folie 16-138
Herunterladen