kapitel 9: graphen und graph-algorithmenertel/vorlesungen/ginf2/graphen-kp09.pdf · graphen:...

34
Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche Transitive Hülle Kürzeste Wege Traveling Salesman-Problem Kapitel 9: Graphen und Graph-Algorithmen _________________________________ PInf II 9.20 Graphen Anwendungsbeispiele: (1) V1 = Menge aller Flughäfen in Deutschland. E1 = { (x,y) V1 x V1 | Es gibt einen Direktflug zwischen x und y } (2) V2 = Menge der Einwohner von Marburg E2 = { (x,y) V2 x V2 | x kennt y } (3) V3 = Menge der Einwohner von Marburg E3 = { (x,y) V3 x V3 | x ist verheiratet mit y }. V3 ist Teilgraph von V2 Ein (gerichteter) Graph ist ein Paar G = <V, E>, wobei gilt: V ist eine endliche Menge von Knoten (engl. Sg.:vertex) und E ist eine zweistellige Relation auf V, d.h. E V x V. Die Elemente von E werden Kanten (engl. Sg.: edge) genannt. Gilt G’ = <V’, E’> mit V’ V und E’ E, so heißt G’ Teilgraph von G.

Upload: ngokien

Post on 07-Feb-2018

227 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

� Graphen: Begriffe und Definitionen

� Bewertete Graphen

� Graphen-Implementierungen

� Tiefen- und Breitensuche

� Transitive Hülle

� Kürzeste Wege

� Traveling Salesman-Problem

Kapitel 9: Graphen und Graph-Algorithmen_________________________________

PInf II 9.20 Graphen

Anwendungsbeispiele: (1) V1 = Menge aller Flughäfen in Deutschland.

E1 = { (x,y) ∈ V1 x V1 | Es gibt einen Direktflug zwischen x und y }(2) V2 = Menge der Einwohner von Marburg

E2 = { (x,y) ∈ V2 x V2 | x kennt y }(3) V3 = Menge der Einwohner von Marburg

E3 = { (x,y) ∈ V3 x V3 | x ist verheiratet mit y }. V3 ist Teilgraph von V2

Ein (gerichteter) Graph ist ein Paar G = <V, E>, wobei gilt:• V ist eine endliche Menge von Knoten (engl. Sg.:vertex) und• E ist eine zweistellige Relation auf V, d.h. E ⊆ V x V. Die Elemente

von E werden Kanten (engl. Sg.: edge) genannt. • Gilt G’ = <V’, E’> mit V’ ⊆ V und E’ ⊆ E, so heißt G’ Teilgraph von G.

Page 2: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.30 Bildliche Darstellung von Graphen

Sei G= <V, E>: • Einen Knoten v ∈ V stellt man durch einen Punkt oder durch einen

kleinen Kreis dar.• Eine Kante (x,y) ∈ E stellt man durch einen Pfeil vom Knoten x zum

Knoten y dar.

Beispiel:

V = { a,b,c,d,e,f,g,h}

E = { (a,d), (d,a), (a,b), (b,c), (c,a), (b,e), (a,e), (f,g), (f,f)}.

a

e

b

d

c

f

gh

PInf II 9.40 Ungerichtete Graphen

• Sei G = <V,E>. Falls für jedes e ∈ E mit e = (v1,v2) gilt: e’ = (v2,v1) ∈E (E ist symmetrisch), so heißt G ungerichteter Graph, ansonsten gerichteter Graph.

• Bei einem ungerichteten Graphen gehört zu jedem Pfeil von x nach y auch ein Pfeil von y nach x. Daher läßt man die Pfeil-spitzen ganz weg und zeichnet nur ungerichtete Kanten.

Beispiel:

V = einige Städte der Umgebung

E = { (x,y) | Es gibt eine direkte Bahnverbindung zwischen x und y }

7

3

4

6

1

2

8

KasselMarburg

Gießen

Frankfurt

Fulda

WürzburgMannheim

5

0

Köln

Bonn

Page 3: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.50

Pfade, Zyklen und Gewichte• Eine Kante k = (x,y) heißt inzident zu x und y.

• Ein Pfad (oder Weg) von x nach y ist eine Folge (x=a0 , a1, ... , ap=y) von Knoten mit (ai , ai+1 ) ∈ E. p wird die Länge des Weges von x nach y genannt.

• In einem einfachen Pfad kommt jeder Knoten höchstens einmal vor.

• Ein Pfad der Länge p ≥ 1 von x nach x, in dem außer x kein Knoten mehr als einmal vorkommt, heißt Zyklus.

• Ein gerichteter Graph <V, E> heißt zyklenfrei oder gerichteter azyklischer Graph (engl: directed acyclic graph, kurz: dag), wenn er keine Zyklen enthält.

• Im ungerichteten Fall schließt man i.a. triviale Zyklen der Form (x,x) oder (x,y,x) aus. Ein ungerichteter Graph ist zyklenfrei, wenn es zwischen jedem Paar von Knoten (x,y) höchstens einen Pfad (ohne triviale Zyklen) gibt.

PInf II 9.60

BeispieleG = <V,E> sei wie oben definiert.

• (b,c,a,d,a) ist ein Pfad von b nach a.

• Er enthält einen Zyklus: (a,d,a).

• (c,a,b,e) ist einfacher Pfadvon c nach e.

• (f,f,f,g) ist ein Pfad.

• (a,b,c,a) und (a,d,a) und (f,f) sind die einzigen Zyklen.

• (a,b,e,a) ist kein Pfad und kein Zyklus.

• <{a,b,c,e}, {(a,b), (b,c), (b,e), (a,e)}> ist ein azyklischer Teilgraph von G.

a

e

b

d

c

f

g

h

Page 4: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.70

Bewertete GraphenEin Graph G = <V, E> kann zu einem bewerteten Graphen G = <V, E, gw(E)> erweitert werden, wenn man eine Gewichtsfunktion

gw: E →→→→ int (oder gw: E →→→→ float/double) hinzunimmt, die jeder Kante e ∈ E ein (positives, ganzzahliges oder reelles) Gewicht gw(e) zuordnet.

7

3

4

6

1

2

8

KasselMarburg

Gießen

Frankfurt

Fulda

WürzburgMannheim

5

0

Köln

Bonn

34

224 88 136

104

30

66

96

93 181 104

174106

Beispiel:len (Marburg, Gießen, Frankfurt, Mannheim) = 184

Für einen Weg w = (x=a0 , a1, ..., ap=y) heißtlen(w) = Σi=0

p-1 gw(ai, ai+1)die bewertete Länge von w.

PInf II 9.80

Ungerichtete Graphen und ZusammenhangUngerichtete Graphen sind Spezialfälle von gerichteten Graphen.

Zusätzlich soll für ungerichtete Graphen gelten: • G heißt zusammenhängend, wenn es zwischen je zwei

(verschiedenen) Knoten einen Weg gibt. • Ist G nicht zusammenhängend, so zerfällt er in eine Vereinigung

zusammenhängender Komponenten (auch Zusammen-hangskomponenten genannt) .

• Ein zusammenhängender zyklenfreier Graph ist ein Baum.

• Eine Gruppe paarweise nicht zusammenhängender Bäume heißt Wald. Jeder zyklenfreie ungerichtete Graph ist also ein Wald.

a

e

b

d

c

f

g

h

Zusammenhangs-komponenten von G

Page 5: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.90

Zusammenhang in gerichteten Graphen� Die Definitionen für Zusammenhang und Zusammenhangskomponenten

lassen sich für gerichteten Graphen ausdehnen: � Ein gerichteter Graph G heißt stark zusammenhängend, wenn es

zwischen je zwei (verschiedenen) Knoten einen Weg gibt. Für einen beliebigen Graphen G kann man die Menge seiner starkenZusammenhangskomponenten betrachten. Zwei Knoten a und b liegen in der gleichen Komponente Z, wenn sowohl ein Weg von a nach b als auch einer von b nach a in Z existiert.

• Ein gerichteter Graph G heißt schwach zusammenhängend, wenn der entsprechende ungerichtete Graph, der aus G durch Hinzunahme aller Rückwärtskanten entsteht, zusammenhängend ist.

Beispiel:Starke Zusammen-hangskomponenten von G

a

e

b

d

cg

h

f

PInf II 9.100

Aufspannender Baum • Ist G ungerichtet und zusammenhängend und R ein zusammenhän-

gender, zyklenfreier Teilgraph von G, der alle Knoten von G enthält, so heißt R ein (auf)spannender Baum (engl.: spanning tree) von G.

Graph mit aufspannendem Baum

Rekursiver Algorithmus SpT zur Konstruktion des (auf)spannen-den Baums für G:

• Markiere einen beliebigen Knoten v ∈ V

• Wiederhole für alle von v ausgehenden Kanten e = (v,v') ∈ E:

Wenn v' unmarkiert, markiere v' und führe SpT(v') aus, sonst lösche e (und gehe zur nächsten Kante weiter).

Page 6: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.110

Repräsentation von Graphen

Für die Repräsentation eines Graphen kommen in erster Linie zweiDatenstrukturen in Frage:

• eine Boolesche Matrix (auch Adjazenzmatrix genannt)• eine Liste oder ein Array von Listen (für die Knoten des Graphen und

deren jeweilige Verbindungen) Repräsentation durch eine Adjazenzmatrix:Ein Graph G = ( V, E) ist i.W. durch die Angabe seiner Kanten E ⊆ V x V

bestimmt. • So wie Teilmengen von V durch Boolesche Arrays dargestellt werden

können, kann man Teilmengen von V × V durch Boolesche Matrizen (sog. Adjazenzmatrizen) darstellen.

• Voraussetzung dafür ist, daß die Menge der Knoten durch einen ordinalen Typ repräsentiert ist.

PInf II 9.120

Graphen: Java-Deklarationen

public class AGraph {

String[] knoBez; // alle Knoten-Bezeichner

int knAnz; // Anzahl der Knoten

boolean[][] aMx; // Adjazemnzmatrix

AGraph (String[] knBez, boolean[][] kanten) {... /* Konstruktor, besetzt Knoten-Bezeichner und

Kanten-Matrix mit den gegebenen Parameter-Werten */}

}

public class AGraph {

String[] knoBez; // alle Knoten-Bezeichner

int knAnz; // Anzahl der Knoten

boolean[][] aMx; // Adjazemnzmatrix

AGraph (String[] knBez, boolean[][] kanten) {... /* Konstruktor, besetzt Knoten-Bezeichner und

Kanten-Matrix mit den gegebenen Parameter-Werten */}

}

Die Klasse AGraph enthält als Basis-Definitionen für die Repräsentation von Graphen durch Adjazentmatrizen:

• ein Datenfeld knBez des Typs Array of String für die Auflistung der Knoten-Bezeichner,

• ein Datenfeld aMx des Typs Array of Array of boolean für die Darstellung der Adjazenzmatrix. Für diese gilt:

aMx[u][v]== true <=> (u,v) ∈∈∈∈ E

Page 7: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.130

Adjazenzmatrix: Beispiel

a

e

b

d

c

f

gh

a c d e f gb hx xx

x xxx

x x

abcdefgh

( x = True, " " = False )

// Konkretisierung zum vorigen Programm

String[] knoBez = {"a","b","c","d","e","f","g","h"};

boolean[][] kanten =

{{false, true, false, true, true, false, false, false},

{false, false, true, false, true, false, false, false},

... // usw. };

AGraph gBsp = new AGraph (knoBez, kanten) ;

...

// Konkretisierung zum vorigen Programm

String[] knoBez = {"a","b","c","d","e","f","g","h"};

boolean[][] kanten =

{{false, true, false, true, true, false, false, false},

{false, false, true, false, true, false, false, false},

... // usw. };

AGraph gBsp = new AGraph (knoBez, kanten) ;

...

PInf II 9.140

Bewertete AdjazenzmatrizenDie Idee der Adjazenzmatrix läßt sich leicht auf bewertete Graphenausdehnen. Statt eines booleschen Werts speichert man das Gewicht gw (u,v) jeder Kante an der betreffenden Position M[u,v] der Matrix M. Man setzt z.B.:

public class BGraph {

String[] knoBez; // Alle Knoten-Bezeichner

int knAnz; // Anzahl der Knoten

int[][] bMx; // Matrix mit Gewichten

BGraph (String[] knBez, int[][] kanten) {

... /* Konstruktor, besetzt knoBez, knAnz und

bMx mit den gegebenen Werten */;

} ...

}..

public class BGraph {

String[] knoBez; // Alle Knoten-Bezeichner

int knAnz; // Anzahl der Knoten

int[][] bMx; // Matrix mit Gewichten

BGraph (String[] knBez, int[][] kanten) {

... /* Konstruktor, besetzt knoBez, knAnz und

bMx mit den gegebenen Werten */;

} ...

}..

M[u,v] =w, falls e =(u,v) ∈ E und gw(e) = w,0, falls u=v und e =(u,u) ∈ E ,∞∞∞∞ (bzw. MAX_VALUE) sonst

{

Page 8: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.150

BN F FD GI KS K MA MR WÜBN 0 181 - - - 34 224 - -

F 181 0 104 66 - - 88 - 136

FD - 104 0 106 96 - - - 93

GI - 66 106 0 - 174 - 30 -

KS - - 96 - 0 - - 104 -

K 34 - - 174 - 0 - - -

MA 224 88 - - - - 0 - -

MR - - - 30 104 - - 0 -

WÜ - 136 93 - - - - - 0

BN F FD GI KS K MA MR WÜBN 0 181 - - - 34 224 - -

F 181 0 104 66 - - 88 - 136

FD - 104 0 106 96 - - - 93

GI - 66 106 0 - 174 - 30 -

KS - - 96 - 0 - - 104 -

K 34 - - 174 - 0 - - -

MA 224 88 - - - - 0 - -

MR - - - 30 104 - - 0 -

WÜ - 136 93 - - - - - 0

Bewertete Adjazenzmatrix: Beispiel

Ist G ein ungerichteter Graph, so ist die Matrix symmetrisch - d.h. man kommt im Prinzip mit einer Dreiecksmatrix aus.

- steht für ∞

PInf II 9.160

String[] knBez = {"Bonn", "Frankfurt", "Fulda", "Gießen",

"Kassel", "Köln", "Mannhem", "Marburg", "Würzburg"};

int[][] kanten = { {0, 181, M, M, M, 34, 224, M, M},

{181, 0, 104, 66, M, M, 88, M, 136},

...

{M, 136, 93, M, M, M, M, M, 0} };

BGraph bahnNetz = new BGraph (knBez, kanten);

String[] knBez = {"Bonn", "Frankfurt", "Fulda", "Gießen",

"Kassel", "Köln", "Mannhem", "Marburg", "Würzburg"};

int[][] kanten = { {0, 181, M, M, M, 34, 224, M, M},

{181, 0, 104, 66, M, M, 88, M, 136},

...

{M, 136, 93, M, M, M, M, M, 0} };

BGraph bahnNetz = new BGraph (knBez, kanten);

7

3

4

6

1

2

8

KasselMarburg

Gießen

Frankfurt

Fulda

WürzburgMannheim

5

0

Köln

Bonn

34

224 88 136

104

30

66

96

93 181

104

174106M steht für ∞ (genauer für:

Integer.MAX_VALUE)

Bewertete Adjazenzmatrix: Initialisierung

Page 9: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.170

Knoten- und Kantenzugriffe Der Zugriff auf einzelne Knoten und Kanten ist mit Hilfe einfacher Zugriffsfunktionen möglich:.

public class BGraph { ... // Forts. von oben

String gibKnoBez (int k) { // Knoten-Zugriff

return knoBez[k];

}

int kantenW (int u, int v) { // Kanten-Zugriff

return bMx[u][v];

}

public class BGraph { ... // Forts. von oben

String gibKnoBez (int k) { // Knoten-Zugriff

return knoBez[k];

}

int kantenW (int u, int v) { // Kanten-Zugriff

return bMx[u][v];

}

Beispiel:

bahnNetz.gibKnoBez(3); // Ergebnis: "Gießen"

bahnNetz.kantenW (3,7); // Ergebnis: 30

bahnNetz.gibKnoBez(3); // Ergebnis: "Gießen"

bahnNetz.kantenW (3,7); // Ergebnis: 30

PInf II 9.180

Adjazenzlisten (1)

Eine zweite (weniger speicheraufwendige) Möglichkeit zur Repräsen-tation eines Graphen besteht darin, jedem Knoten eine Liste seiner Nachbarknoten - ggf. mit den zugehörigen Kantengewichten - zuzu-ordnen. Eine solche Liste wird auch Adjazenzliste genannt.

a

b

d

e

a

e

b

d

c

f

gh

b

c

e

c

a

d

a

e f g h

f

g

Page 10: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.190

b d e

class VerbListe {

int ziel; // Endpunkt-Nrint gew; // Kanten-Gewicht

VerbListe nx; // Nachfolger-Referenz

VerbListe (int z, int w, VerbListe v) {ziel = z; gew = w; nx = v;

}

}

class LKnoten {

String knBez;

VerbListe nachbarn;

LKnoten (String s, VerbListe v) {

knBez = s; nachbarn = v;

}

}

class VerbListe {

int ziel; // Endpunkt-Nrint gew; // Kanten-Gewicht

VerbListe nx; // Nachfolger-Referenz

VerbListe (int z, int w, VerbListe v) {ziel = z; gew = w; nx = v;

}

}

class LKnoten {

String knBez;

VerbListe nachbarn;

LKnoten (String s, VerbListe v) {

knBez = s; nachbarn = v;

}

}

a b d e

5 7 2

5 7 2

2 4 5

2 4 5

knBez

nachbarn

gew

ziel

Adjazenzlisten: Klassendefinitionen (1)

PInf II 9.200

public class LGraph {

static final int maxInt = Integer.MAX_VALUE;

LKnoten[] knoten; // Array von Knoten(-Referenzen)

int knAnz;

LGraph (LKnoten[] knL) { // besetzt Nachbarlisten

knAnz = knL.length;

knoten = new LKnoten[knAnz];

for (int i = 0; i < knAnz; i++) knoten[i] = knL[i];

}

int kantenW (int u, int v) { /* liefert gew, falls

direkte Verbindung, sonst maxInt */

if (u == v) return 0;

VerbListe vLi = knoten[u].nachbarn;

while (vLi != null) {

if (vLi.ziel == v) return vLi.gew;

vLi = vLi.nx; }

return maxInt;

} ...

}

public class LGraph {

static final int maxInt = Integer.MAX_VALUE;

LKnoten[] knoten; // Array von Knoten(-Referenzen)

int knAnz;

LGraph (LKnoten[] knL) { // besetzt Nachbarlisten

knAnz = knL.length;

knoten = new LKnoten[knAnz];

for (int i = 0; i < knAnz; i++) knoten[i] = knL[i];

}

int kantenW (int u, int v) { /* liefert gew, falls

direkte Verbindung, sonst maxInt */

if (u == v) return 0;

VerbListe vLi = knoten[u].nachbarn;

while (vLi != null) {

if (vLi.ziel == v) return vLi.gew;

vLi = vLi.nx; }

return maxInt;

} ...

}

Adjazenzlisten: Klassendefinitionen (2)

String gibKnoBez (int k) {

return knoten[k].knBez;

} ...

String gibKnoBez (int k) {

return knoten[k].knBez;

} ...

Page 11: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.210

Beispiel:

LKnoten[] knoten = { new LKnoten ("Bonn",

new VerbListe(1, 181,

new VerbListe(5, 34,

new VerbListe(6, 224,

null )))),

....

// usw.

new LKnoten ("Würzburg",

new VerbListe(1, 136,

new VerbListe(2, 93,

null )))

};

LGraph bahnNetz = new LGraph(knoten);

LKnoten[] knoten = { new LKnoten ("Bonn",

new VerbListe(1, 181,

new VerbListe(5, 34,

new VerbListe(6, 224,

null )))),

....

// usw.

new LKnoten ("Würzburg",

new VerbListe(1, 136,

new VerbListe(2, 93,

null )))

};

LGraph bahnNetz = new LGraph(knoten);

7

3

4

6

1

2

8

KasselMarburg

Gießen

Frankfurt

Fulda

WürzburgMannheim

5

0

Köln

Bonn

34

224 88 136

104

30

66

96

93

181104

174106

Adjazenzliste: Anwendung

PInf II 9.220

Verkehrsnetz als Array von Adjazenzlisten

Bonn0 1 181 5 34 6 224 nil

Frankfurt1 0 181 2 104

nil

Fulda2 1 104 3 106 nil

Gießen3 1 66 2 106 5 174

Kassel4 2 96 7 104 nil

Köln5 0 34 3 174 nil

Mannheim6 0 224 1 88 nil

Marburg7 3 30 4 104 nil

Würzburg 8 1 136 2 93 nil

3 66 6 88

8 136

4 96 8 93

nil7 30

Page 12: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.230

Eine dritte Möglichkeit zur Implementierung von Graphen besteht darin, auch die Folge der Knoten auf eine Liste abzubilden, d.h. der gesamte Graph wird durch eine Liste von Listen dargestellt.

Implementierung durch Listen von Listen

a

e

b

d

c

f

gh

a b d e

b c e

c a

e

f f g

d a

g

h

PInf II 9.240

public class LLGraph {

String grBez; // Graph-Bezeichner

LLKnoten kn; // Liste der Knoten des Graphen

int knAnz;

LLGraph (String bez) { // erzeugt Graphen namens bez

grBez = bez;

kn = null;

knAnz = 0;

}

public LLKnoten fuegeKnEin (String knBez) {

// fügt Knoten knBez in die Liste ein

kn = new LLKnoten (knBez, kn);

knAnz ++;

return kn;

} // end fuegeKnEin

.... // weiter s. nächste Folie

public class LLGraph {

String grBez; // Graph-Bezeichner

LLKnoten kn; // Liste der Knoten des Graphen

int knAnz;

LLGraph (String bez) { // erzeugt Graphen namens bez

grBez = bez;

kn = null;

knAnz = 0;

}

public LLKnoten fuegeKnEin (String knBez) {

// fügt Knoten knBez in die Liste ein

kn = new LLKnoten (knBez, kn);

knAnz ++;

return kn;

} // end fuegeKnEin

.... // weiter s. nächste Folie

Graph als Liste von Listen (1)

Page 13: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.250

// Forts. von voriger Folie

public class LLKnoten {

String knBez;

NbListe nachbarn; // Adjazenzliste für ausg. Kanten

LLKnoten nxKn; // Ref. auf nächsten Knoten

LLKnoten (String s, LLKnoten nx) { // Konstruktor

knBez = s;

nachbarn = null;

nxKn = nx;

} ... // end Konstruktor

public void fuegeNbEin (LLKnoten ziel, int gw) {

// Kante vom akt. Knoten zu Kn. ziel mit Gewicht gw

nachbarn = new NbListe (ziel, gw, nachbarn);

} .... // end fuegeNbEin

} // end LLKnoten

// Forts. von voriger Folie

public class LLKnoten {

String knBez;

NbListe nachbarn; // Adjazenzliste für ausg. Kanten

LLKnoten nxKn; // Ref. auf nächsten Knoten

LLKnoten (String s, LLKnoten nx) { // Konstruktor

knBez = s;

nachbarn = null;

nxKn = nx;

} ... // end Konstruktor

public void fuegeNbEin (LLKnoten ziel, int gw) {

// Kante vom akt. Knoten zu Kn. ziel mit Gewicht gw

nachbarn = new NbListe (ziel, gw, nachbarn);

} .... // end fuegeNbEin

} // end LLKnoten

Graph als Liste von Listen (2)

PInf II 9.260

// Forts. von voriger Folie

public class NbListe {

LLKnoten kn; // Nachbarknoten, Ziel einer Kante

int gw; // Kantengewicht für diese Kante

NbListe nxKn; // Referenz auf nächsten Nachbarn

NbListe (LLKnoten zi, int g, NbListe nx) {

kn = zi;

gw = g;

nxKn = nx;

}... // end Konstruktor

} // end NbListe

// Forts. von voriger Folie

public class NbListe {

LLKnoten kn; // Nachbarknoten, Ziel einer Kante

int gw; // Kantengewicht für diese Kante

NbListe nxKn; // Referenz auf nächsten Nachbarn

NbListe (LLKnoten zi, int g, NbListe nx) {

kn = zi;

gw = g;

nxKn = nx;

}... // end Konstruktor

} // end NbListe

Graph als Liste von Listen (3)

Anwendungsbeispiel:

LLGraph bNetz = new LLGraph ("Bahnnetz");

LLKnoten bn = bNetz.fuegeKnEin ("Bonn");

LLKnoten f = bNetz.fuegeKnEin ("Frankfurt"); ...

bn.fuegeNbEin (f, 181);

f.fuegeNbEin (bn, 181); ...

LLGraph bNetz = new LLGraph ("Bahnnetz");

LLKnoten bn = bNetz.fuegeKnEin ("Bonn");

LLKnoten f = bNetz.fuegeKnEin ("Frankfurt"); ...

bn.fuegeNbEin (f, 181);

f.fuegeNbEin (bn, 181); ...

Page 14: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.270

Alle hier betrachteten Möglichkeiten zur Implementierung von Graphen haben ihre spezifischen Vor- und Nachteile. Seien n = Knotenzahl und m = Kantenzahl eines Graphen G.

Vergleich der Implementierungen

Vorteile Nachteile

Adjazenz-matrix

Adjazenz-liste

Liste von Listen

Berechnung der Inzidenz mit O(1)

Platzbedarf beträgt nur O(n+m)

Knoten lassen sich flexibel hinzufügen/ löschen

hoher Platzbedarf und teure Initialisierung: beide O(n2)

Effizienz der Kantensuche abhängig von Knotenordnung

Effizienz von Knoten- und Kantensuche abhängig von Listenposition

PInf II 9.280

Traversieren von Graphen

Für die Traversierung betrachten wir die folgenden Strategien: :Tiefensuche (depth first search) Breitensuche (breadth first search)

• Tiefensuche entspricht der Baum-Traversierung in Vorordnung, Breitensuche derjenigen in Ebenen-Ordnung.

• Alle folgenden Algorithmen können sowohl auf gerichtete als auch auf ungerichtete Graphen angewendet werden. Dabei setzen wir voraus,daß alle Graphen zusammenhängend sind.

• Viele Algorithmen auf Graphen beruhen darauf, daß man alle Knoten (oder alle Kanten) des Graphen durchläuft (den Graphen traversiert).

• Solche Traversierungen funktionieren ähnlich wie entsprechende Baum-Traversierungen, doch muß man bei Graphen darauf achten, daß man nicht in Endlos-Schleifen gerät, wenn der Graph Zyklen hat. Daher markiert man bereits besuchte Knoten.

Page 15: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.290 Tiefensuche (1)

Tiefensuche läßt sich am einfachsten implementieren: Wir betrachten sowohl eine rekursive Implementierung als auch eine mit einem Stack. Der folgende Algorithmus Depth-First-Visit besucht alle Knoten, die von einem Ausgangsknoten mit der Nummer k aus erreichbar sind. Wir setzen voraus, daß zu Beginn alle Knoten unmarkiert sind.

void depFVisit (int k) {

if ( ...) // k ist noch nicht markiert

markiere(k);

bearbeite (k);

for ( .. /* alle i, die Nachbarn von k sind */ )

depFVisit (i);

}

void depFVisit (int k) {

if ( ...) // k ist noch nicht markiert

markiere(k);

bearbeite (k);

for ( .. /* alle i, die Nachbarn von k sind */ )

depFVisit (i);

}

Programmgerüst:

Konkreter Code hängt von der Graph-Implementierung ab.

PInf II 9.300 Tiefensuche (2)

Zum Markieren benutzen wir ein Feld der Länge knAnz. Als Typ der Feldelemente wählen wir int, um den Algorithmus leicht auf Mehrfachbesuche erweitern zu können.

void depFVisit (int k) {

int[] marken = new int [knAnz];

for (int i = 0; i < knAnz; i++) marken [i] = 0;// initialisiere Marken

rekDFVisit (marken, k);}

void rekDFVisit (int[] marken, int k) {if (marken[k] < 1) { // k ist noch nicht markiert

marken [k] ++;System.out.println (knoten[k].knBez);

// bezieht sich auf Klasse LGraphfor (int i = 0; i < knAnz; i++)

if (kantenW (k, i) < maxInt) // i ist Nachbar von krekDFVisit (marken, i);

}}

void depFVisit (int k) {

int[] marken = new int [knAnz];

for (int i = 0; i < knAnz; i++) marken [i] = 0;// initialisiere Marken

rekDFVisit (marken, k);}

void rekDFVisit (int[] marken, int k) {if (marken[k] < 1) { // k ist noch nicht markiert

marken [k] ++;System.out.println (knoten[k].knBez);

// bezieht sich auf Klasse LGraphfor (int i = 0; i < knAnz; i++)

if (kantenW (k, i) < maxInt) // i ist Nachbar von krekDFVisit (marken, i);

}}

Page 16: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.310 Tiefensuche (Beispiel)

Falls wir die Nachbarn der Knoten in alphabetischer Reihen-folge erzeugen, führt der Aufruf

depFVisit (0)

zur Ausgabe aller Städte in der folgenden Reihenfolge :Bonn, Frankfurt, Fulda, Gießen, Köln, Marburg, Kassel, Würzburg, Mannheim.

7

3

4

6

1

2

8

KasselMarburg

Gießen

Frankfurt

Fulda

WürzburgMannheim

5

0

Köln

Bonn

Der Aufruf depFVisit (2)

führt zur Ausgabe :Fulda, Frankfurt, Bonn, Köln, Gießen, Marburg, Kassel, Mannheim,Würzburg.

PInf II 9.320 Tiefensuche mit Stack

Wenn Nachbarn in alphabetischer Reihenfolge erzeugt werden, führt dFSVisit (7) zu Bearbeitung und push-Operationen in folgender Reihenfolge: Marburg, Gießen, Frankfurt , Bonn, Köln, Mannheim, Fulda, Kassel, Würzburg.

void dFSVisit (int k) {Stack st = new Stack(knAnz);markiere(k); bearbeite (k);st.push(k);while (! st.istLeer()) {

int akt = st.top();if ( ... ) {

/* es existiert noch nichtmark. Nachbar j von akt */

akt = j;markiere(akt);bearbeite(akt);st.push(akt);}else st.pop();

}

}

void dFSVisit (int k) {Stack st = new Stack(knAnz);markiere(k); bearbeite (k);st.push(k);while (! st.istLeer()) {

int akt = st.top();if ( ... ) {

/* es existiert noch nichtmark. Nachbar j von akt */

akt = j;markiere(akt);bearbeite(akt);st.push(akt);}else st.pop();

}

}

7

3

4

6

1

2

8

KasselMarburg

Gießen

Frankfurt

Fulda

WürzburgMannheim

5

0

Köln

Bonn

Page 17: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.330 Breitensuche mit QueueÄhnlich wie bei der Baum-Traversierung in Ebenen-Ordnung wird eine Warteschlange als Hilfsspeicher verwendet.

void bFVisit (int k) {Queue q = new Queue(knAnz);markiere(k);q.enQueue(k);while (! q.istLeer()) {

int akt = q.top();q.deQueue();bearbeite(akt);for ( /* alle noch nichtmark. Nachbarn j von akt */ ){ markiere(j);q.enQueue(j); }

}

}

void bFVisit (int k) {Queue q = new Queue(knAnz);markiere(k);q.enQueue(k);while (! q.istLeer()) {

int akt = q.top();q.deQueue();bearbeite(akt);for ( /* alle noch nichtmark. Nachbarn j von akt */ ){ markiere(j);q.enQueue(j); }

}

}

7

3

4

6

1

2

8

KasselMarburg

Gießen

Frankfurt

Fulda

WürzburgMannheim

5

0

Köln

Bonn

Wenn Nachbarn in alphabetischer Reihenfolge erzeugt werden, führtBFVisit(Marburg) zur Reihenfolge:Marburg, Gießen, Kassel, Frankfurt, Fulda, Köln, Bonn, Mannheim,Würzburg.

PInf II 9.340 Breitensuche mit Queue: CodeFür die Markierung wird wieder ein int-Array Marken der Länge knAnzverwendet:

void bFVisit (int k) {

int[] marken = new int [knAnz];

for (int i = 0; i < knAnz; i++) marken[i] = 0;

// initialisiere Marken

Queue q = new Queue(knAnz);

marken[k] ++;

q.enQueue(k);

while (! q.istLeer()) {

int akt = q.top();

q.deQueue();

System.out.println (knoten[akt].knBez);

for (int j = 0; j < knAnz; j++)

if (kantenW(akt, j) < maxInt && marken[j] < 1)

{ marken[j] ++; q.enQueue(j); }

}

}

void bFVisit (int k) {

int[] marken = new int [knAnz];

for (int i = 0; i < knAnz; i++) marken[i] = 0;

// initialisiere Marken

Queue q = new Queue(knAnz);

marken[k] ++;

q.enQueue(k);

while (! q.istLeer()) {

int akt = q.top();

q.deQueue();

System.out.println (knoten[akt].knBez);

for (int j = 0; j < knAnz; j++)

if (kantenW(akt, j) < maxInt && marken[j] < 1)

{ marken[j] ++; q.enQueue(j); }

}

}

Page 18: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.350 Transitive HülleEine zweistellige Relation R auf einer Menge V heißt transitiv, falls gilt :

∀∀∀∀ x, y, z ∈∈∈∈ V : (x,y) ∈∈∈∈ R und (y,z) ∈∈∈∈ R →→→→ (x,z) ∈∈∈∈ R.Die transitive Hülle t(R) einer zweistelligen Relation R auf V ist die kleinste Relation Q, für die gilt: Q ist transitiv und R ⊆ Q.

Faßt man R als Kantenmenge eines Graphen G über V auf, so sei t(G) = (V, t(R)) und es gilt:

• Es gibt in eine Kante (x,y) ∈∈∈∈ t(R) ↔ in G existiert ein Pfad von x nach y.Das heißt, t(G) gibt direkt Auskunft darüber, zwischen welchen Knoten-Paaren von G Pfade existieren. Wie kann man t(G) berechnen?

A

E

B

D

C

F

GH

A

E

B

D

C

F

GH

Ausgangsgraph G transitive Hülle t(G)

PInf II 9.360

Warshall's AlgorithmusWarshall's Algorithmus berechnet die transitive Hülle einer Relation (eines Graphen), die durch eine boolesche Adjazenzmatrix aMxdargestellt ist. Die Matrix aMx wird dabei schrittweise zur transitiven Hülle aufgefüllt:

void warshall () { // berechnet trans. Hülle für Matrix AMx

boolean[][] aMx = {..// aMx[u][v]:= true <=> (u,v) ∈∈∈∈ E};

for (int y = 0; y < knAnz; y++)for (int x = 0; x < knAnz; x++)

if (aMx[x][y])for (int z = 0; z < knAnz; z++)

if (aMx[y][z]) aMx[x][z] = true;

}

void warshall () { // berechnet trans. Hülle für Matrix AMx

boolean[][] aMx = {..// aMx[u][v]:= true <=> (u,v) ∈∈∈∈ E};

for (int y = 0; y < knAnz; y++)for (int x = 0; x < knAnz; x++)

if (aMx[x][y])for (int z = 0; z < knAnz; z++)

if (aMx[y][z]) aMx[x][z] = true;

}

Frage: Funktioniert dieser Algorithmus auch, wenn man so schachtelt :

for (int x = 0; x < knAnz; x++)

for (int y = 0; y < knAnz; y++) ....

for (int x = 0; x < knAnz; x++)

for (int y = 0; y < knAnz; y++) ....?

Page 19: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.370

Korrektheit von Warshall's Algorithmus (1)Die Korrektheit läßt sich in 3 Schritten nachweisen:

• Schritt 1 - Terminierung: Die 3 Schleifen laufen jeweils bis zu einer festen, unveränderlichen Schranke → Terminierung i.O.

• Schritt 2 - Fundiertheit: Der Algorithmus stellt unter bestimmten Voraussetzungen neue Kanten her. Da er nur dann eine neue Kante (x,z) schafft (d.h. aMx[x,z] auf true setzt), wenn bereits (x,y) und (y,z) ∈∈∈∈ R sind (d.h. aMx[x,y] = true und aMx[y,z] = truegelten), tut er sicherlich nichts Falsches.

• Schritt 3 - Vollständigkeit: Es bleibt zu zeigen, daß er genug tut, d.h. daß am Ende tatsächlich die (vollständige) transitive Hülle erzeugt wurde.

Dieser Teil des Beweises nutzt vollständige Induktion über die Mächtigkeit der Menge von Zwischenknoten auf dem Wege zwischen zwei beliebigen Knoten u und v.

PInf II 9.380

Korrektheit von Warshall's Algorithmus (2)• Für den Beweis der Vollständigkeit der erzeugten Verbindungen spielt

es eine ausschlaggebende Rolle, daß die äußere for-Schleife über den Zwischenknoten y läuft.

• Wir formulieren die folgende Invariante der äußeren for-Schleife (in Abhängigkeit von y, Knoten werden o.B.d.A. mit ihren Nummern identifiziert).

I(y) = “∀ u, v ∈ V : Wenn ein Weg von u nach v existiert, dessen sämtliche Zwischenknoten in Z = {0, .. y-1} sind, dann gilt: aMx[u,v] = true.”

I(y) = “∀ u, v ∈ V : Wenn ein Weg von u nach v existiert, dessen sämtliche Zwischenknoten in Z = {0, .. y-1} sind, dann gilt: aMx[u,v] = true.”

Der Beweis erfolgt durch Induktion über die Kardinalität n der Menge Z.

(1) Z = {}. Es existiert ein Weg von u nach v ohne Zwischenknoten →→→→aMx[u,v] = true.

(2) Die Behauptung gelte für Z = {0, .. y-1}

Page 20: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.390 Korrektheit von Warshall's Algorithmus (3)

� Es sei ein beliebiger Weg von u nach v gegeben, dessen Zwischenknoten alle in Z = {0, .. y} liegen.

� Wenn y als Zwischenknoten nicht vorkommt, gilt nach I.V.: aMx[u,v] =true.

� Sei also y ein Zwischenknoten auf dem Weg von u nach v :

v u y . . ... .. . . . . . ... .. .

alle Zwischenknoten mit Nr. < y

Aus I(y) folgt, daß bereits gilt :

aMx[u,y] und aMx[y,v]

Beim nächsten Schleifendurchlauf (mit x=u, z=v) ...... wird aMx[u,v]auf true gesetzt und damit gilt : I(y+1)

for (int x = 0; x < knAnz; x++);if (aMx [x][y])

for (int z = 0; z < knAnz; z++)if (aMx [y][z]) aMx [x][z] = true;

for (int x = 0; x < knAnz; x++);if (aMx [x][y])

for (int z = 0; z < knAnz; z++)if (aMx [y][z]) aMx [x][z] = true;

PInf II 9.400

Wege in bewerteten Graphen� In einem bewerteten Graphen hatten wir bereits zu einem gegebenen

Weg zwischen zwei Knoten u und v die (bewertete) Länge des Weges definiert:

In unserem Beispiel gilt etwa:len(Marburg, Gießen, Frankfurt, Mannheim) = 184 und:len(Marburg, Gießen, Köln, Bonn, Mannheim) = 462

W = (u=k0 , k1, ..., kp=v)

len(W) = gw(k0,k1) + ... + gw(kp-1,kp)

� Für die Definition einer Entfernung ist diese Definition jedoch noch unbefriedigend, da wir verschiedene Wege mit gleichem Anfang und Ende haben können:

� Als Entfernung für zwei Knoten u und v in einem bewerteten zusammenhängenden Graphen definieren wir daher das Minimum der Längen aller möglichen Wege, d.h. die Länge des kürzesten Weges.

Page 21: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.410

Kürzeste Wege

Ein Weg (u = a0 , a1, ... , ap = v) zwischen zwei Knoten u und v heißt kürzester Weg , wenn seine bewertete Länge ΣΣΣΣi gw(ki, ki+1) minimal ist.

Dieser Wert ist die (kürzeste) Entfernung von u nach v.

Ein Weg (u = a0 , a1, ... , ap = v) zwischen zwei Knoten u und v heißt kürzester Weg , wenn seine bewertete Länge ΣΣΣΣi gw(ki, ki+1) minimal ist.

Dieser Wert ist die (kürzeste) Entfernung von u nach v.

• Um die kürzeste Entfernung zwischen je zwei Knoten eines Graphen zu finden (engl.: all pairs shortest path problem), kann man Warshall'sAlgorithmus leicht verändern. Dieser Algorithmus wird gewöhnlich R.W. Floyd zugeschrieben.

• Anstelle der booleschen Matrix verwenden wir jetzt wieder eine bewertete Adjazenzmatrix bMx und setzen dabei gw (u,v) = ∞ (bzw. = maxInt) , falls (u,v) ∉ E:

int[][] bMx = {... // bMx[u][v]:= gw(u,v) für (u,v) ∈∈∈∈ E,

maxInt sonst };

int[][] bMx = {... // bMx[u][v]:= gw(u,v) für (u,v) ∈∈∈∈ E,

maxInt sonst };

PInf II 9.420

Floyd's Algorithmus: Code

I(y) = "∀ u, v ∈ V : bMx[u,v] = Länge des kürzesten Weges, der nur Zwischenknoten aus {0 .. y-1} benutzt.”

I(y) = "∀ u, v ∈ V : bMx[u,v] = Länge des kürzesten Weges, der nur Zwischenknoten aus {0 .. y-1} benutzt.”

Invariante :Komplexität von Floyd'sAlgorithmus: O (N3)

void kWegFloyd () {

// berechnet kürzeste Wege für bewertete Adj.-Matrix bMxint[][] bMx = {... }; // vgl. oben

for (int y = 0; y < knAnz; y++);for (int x = 0; x < knAnz; x++);

if (bMx[x][y] < M)for (int z = 0; z < knAnz ; z++)

if ((bMx[y][z] < M) &&bMx[x][y] + bMx[y][z] < bMx[x][z])

bMx[x][z] = bMx[x][y] + bMx[y][z];

}

void kWegFloyd () {

// berechnet kürzeste Wege für bewertete Adj.-Matrix bMxint[][] bMx = {... }; // vgl. oben

for (int y = 0; y < knAnz; y++);for (int x = 0; x < knAnz; x++);

if (bMx[x][y] < M)for (int z = 0; z < knAnz ; z++)

if ((bMx[y][z] < M) &&bMx[x][y] + bMx[y][z] < bMx[x][z])

bMx[x][z] = bMx[x][y] + bMx[y][z];

}

Page 22: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.430

Dijkstra’s AlgorithmusEin weiterer Algorithmus berechnet für einen vorgegebenen Knoten u die kürzeste Entfernung zu allen anderen Knoten (engl.: single source shortest path problem). Dieser Algorithmus stammt von E.W.Dijkstra(1959). Ist man nur am kürzesten Weg zu einem bestimmten Knoten vinteressiert, so kann man i.a. vorzeitig abbrechen.Grundidee des Algorithmus:Eine Menge S ⊆⊆⊆⊆ V (im Bild rechts grüneingefärbt) beschreibt den jeweilsbereits bearbeiteten Teilgraphenvon G. Anfangs ist S = {u}• S wird schrittweise um je einen Knoten erweitert, so daß für alle k ∈ S gilt: Der kürzeste Weg von u nach k verläuft ausschließlich über Knoten von S. • NK = {nk | nk ist mit mindestens einem k ∈ S direkt verbunden} (im Bild gelb eingefärbt) ist die Menge der Nachbar- oder Kandidatenknoten zur Erweiterung von S. Aus K wird jeweils derjenige Knoten ausgewählt (und S zugeschlagen), der minimalen Abstand zu u hat.

S u nk'

nk k

k'

G

PInf II 9.440

intSet S = {u}; int [knAnz] minEntf = {∞∞∞∞, ∞∞∞∞, ..., ∞∞∞∞}; while (¬¬¬¬ v ∈∈∈∈ S) {

Finde k ∈∈∈∈ S und nk ∈∈∈∈ (V - S) mitminEntf[k] + bMx[k,nk] ist minimal ;minEntf[nk] = minEntf[k] + bMx[k,nk];S = S ∪∪∪∪ {nk };

}// minEntf[v] = Minimale Distanz von u nach v

intSet S = {u}; int [knAnz] minEntf = {∞∞∞∞, ∞∞∞∞, ..., ∞∞∞∞}; while (¬¬¬¬ v ∈∈∈∈ S) {

Finde k ∈∈∈∈ S und nk ∈∈∈∈ (V - S) mitminEntf[k] + bMx[k,nk] ist minimal ;minEntf[nk] = minEntf[k] + bMx[k,nk];S = S ∪∪∪∪ {nk };

}// minEntf[v] = Minimale Distanz von u nach v

Invariante :

∀∀∀∀ s ∈∈∈∈

S : minEntf[s] = kürzeste Entfernung von u nach s.

Invariante :

∀∀∀∀ s ∈∈∈∈

S : minEntf[s] = kürzeste Entfernung von u nach s.

Dijkstra’s Algorithmus: Programmgerüst

Komplexitätsbetrachtung:Im ungünstigsten Fall wird die while-Schleife n = knAnz-mal durchlaufen. Das Bestimmen des nächsten Knotens nk für S erfordert beim obigen (nicht optimierten) Algorithmus einen Aufwand von O(N2). Damit liegt der Gesamt-aufwand wieder bei O(N3). Dijkstra gibt allerdings eine Verbesserung seines Algorithmus an, die den Aufwand im durchschnittlichen Fall auf O(N2) drückt.

Page 23: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.450

Implementierung von Dijkstra’s Algorithmus

Die Menge S wird mit Hilfe eines booleschen Array S der Länge knAnzgespeichert. Es gilt S[k] = true ↔ k ∈ S.

Weiter wird ein int-Array minEntf der Länge anz zum Abspeichern der kürzesten Entfernung zu u für alle Knoten von S benötigt. minEntf kann mit 0 initialisiert werden, da später nur auf bereits in S liegende Elemente zurückgegriffen wird.

Zu Beginn ist S = {u} und NK = {nk | es exisitiert eine Kante (u, nk) }In jedem Schritt der äußeren (while-) Schleife wird ein Knotenpaar (k ∈

S, nk ∈ NK) so bestimmt, daß minEntf [k] + bMx [k][nk] minimal ist. kNeu = nk ist ein neuer Knoten, zu dem die kürzeste Entfernung kNeuEntf von u ermittelt wurde. kNeu wird zu S hinzugenommen und kNeuEntf wird in minEntf [kNeu] aufgenommen.

Der Algorithmus terminiert, sobald kNeu der Zielknoten v ist.

PInf II 9.460 Dijkstra’s Algorithmus: Code

void kWegDijkstra (int u, int v) { // kürz.Weg von u nach v

boolean[] s = new boolean[knAnz];for (int i = 0; i < knAnz; i++) s[i] = false;s[u] = true;System.out.println("Kürzeste Entfernung von "+ knoBez[u]);int[] minEntf = new int[knAnz];minEntf[u] = 0;while (! s[v]) { // terminiert, wenn Ziel v erreicht

int min = M; int kNeu = 0;for (int k = 0; k < knAnz; k++)

if (s[k])for (int nk = 0; nk < knAnz; nk++)

if (! s[nk] && bMx[k][nk] < maxInt) {int kNeuEntf = minEntf[k] + bMx[k][nk] ;if (kNeuEntf < min) {min = kNeuEntf; kNeu = nk;}

}s[kNeu] = true; // kNeu wird in S aufgenommenminEntf[kNeu] = min;System.out.println (" nach " + knoBez[kNeu] + " ist "+ min + " km.");

}

}

void kWegDijkstra (int u, int v) { // kürz.Weg von u nach v

boolean[] s = new boolean[knAnz];for (int i = 0; i < knAnz; i++) s[i] = false;s[u] = true;System.out.println("Kürzeste Entfernung von "+ knoBez[u]);int[] minEntf = new int[knAnz];minEntf[u] = 0;while (! s[v]) { // terminiert, wenn Ziel v erreicht

int min = M; int kNeu = 0;for (int k = 0; k < knAnz; k++)

if (s[k])for (int nk = 0; nk < knAnz; nk++)

if (! s[nk] && bMx[k][nk] < maxInt) {int kNeuEntf = minEntf[k] + bMx[k][nk] ;if (kNeuEntf < min) {min = kNeuEntf; kNeu = nk;}

}s[kNeu] = true; // kNeu wird in S aufgenommenminEntf[kNeu] = min;System.out.println (" nach " + knoBez[kNeu] + " ist "+ min + " km.");

}

}

Page 24: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.470

Beweis der Invarianten∀∀∀∀ x ∈∈∈∈ S : minEntf[x] = kürzeste

Entfernung von u nach x.

Finde k ∈ S und nk ∈ (V - S) mit

minEntf[k] + BMx[k,nk] ist minimal ;

minEntf[nk] = minEntf[k] + BMx[k,nk];

∀∀∀∀

x ∈∈∈∈ S ∪∪∪∪ {nk} : minEntf[x] = kürzeste Entfernung von u nach x.

Zu zeigen : Es kann keine kürzere Verbindung von u zu nk geben als die über k .

Angenommen, es gäbe eine kürzereVerbindung u, ..., nk, dann sei k' der letzte Zwischenknoten aus S auf diesem Weg und nk' der nächste, also u, ..., k', nk', ... , nk.

Dann wäre aber der Weg von u nach nk' kürzer und statt nk wäre nk'gefunden worden !

Su

nk'

nk k

k'

G

PInf II 9.480

S minEntf k kNeu min{0} {0, 0, 0, 0, 0, 0, 0, 0, 0} 0 M

(1) {0} {0, 0, 0, 0, 0, 0, 0, 0, 0} 0 5 34(2) {0, 5} {0, 0, 0, 0, 0, 34, 0, 0, 0} 0 1 181(3) {0, 1, 5} {0,181, 0, 0, 0, 34, 0, 0, 0} 5 3 208(4) {0, 1, 3, 5} {0,181, 0, 208, 0, 34, 0, 0, 0} 0 6 224(5) {0, 1, 3, 5, 6} {0,181, 0, 208, 0, 34, 224, 0, 0} 3 7 238(6) {0, 1, 3, 5, 6, 7} {0,181, 0, 208, 0, 34, 224, 238, 0} 1 2 285(7) {0, 1, 2, 3, 5, 6, 7} {0,181, 285, 208, 0, 34, 224, 238, 0} 1 8 317(8) {0, 1, 2, 3, 5, 6, 7, 8} {0,181, 285, 208, 0, 34, 224, 238, 317} 7 4 342(9) {0, 1, 2, 3, 4, 5, 6, 7, 8}

7

3

4

6

1

2

8

KasselMarburg

Gießen

Frankfurt

Fulda

WürzburgMannheim

5

0

Köln

Bonn

34

224 88 136

104

30

66

96

93 181 104

174106 Beispiel: Kürzester Weg

von Bonn nach Kassel

Page 25: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.490

Ist v der letzte (d.h. von u am weitesten entfernte) Knoten des Graphen G (wie im vorangegangenen Beispiel) so liefert Dijkstra's Algorithmus offenbar einen aufspannenden Baum für G - und zwar gerade denjenigen, der für jeden Knoten k den kürzesten Weg von u nach k darstellt.

7

3

4

6

1

2

8

KasselMarburg

Gießen

Frankfurt

Fulda

WürzburgMannheim

5

0

Köln

Bonn

34

22488 136

104

30

66

96

93 181

104

174106

Kürzester Weg und aufspannender Baum

PInf II 9.500 Bewertete Graphen: ein weiteres Beispiel

Richmond

Oakland

Hayward

Fremont

San Jose

San Rafael

San Francisco

San Mateo

Palo Alto

15

1518

12

18

2020

20

10

15 20

14

12

0 3

5

74

68

10 Scotts Valley

12Santa Cruz

9Santa Clara

1513Half Moon Bay

11Watsonville

14Pacifica

15

15

25

50 35

10 70

60

Page 26: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.510 Adjazenzmatrix für SFBay-Beispiel

0 1 2 3 4 5 6 7 8 9 10 11 12 13 140 0 18 - 12 20 - - - - - - - - - 151 18 0 15 - - - - - - - - - - - -2 - 15 0 15 - - - - - - - - - - -3 12 - 15 0 - 20 - - - - - - - - -4 20 - - - 0 20 18 - - - - - - 25 -5 - - - 20 20 0 - 14 - - - - - - -6 - - - - 18 - 0 15 - 10 - - - - -7 - - - - - 14 15 0 20 - - - - - -8 - - - - - - - 20 0 15 - 60 - - -9 - - - - - - 10 - 15 0 35 - - - -10 - - - - - - - - - 35 0 - 10 - -11 - - - - - - - - 60 - - 0 70 - -12 - - - - - - - - - - 10 70 0 50 -13 - - - - 25 - - - - - - - 50 0 1514 15 - - - - - - - - - - - - 15 0

PInf II 9.520 Speicherung von Graphen als Array:SFBay-Beispielprivate static final int M = Integer.MAX_VALUE;

String[] SFKn = {"San Francisco", "San Rafael", "Richmond", "Oakland","San Mateo","Hayward", "Palo Alto", "Fremont", "San Jose", "SantaClara", "Scotts Valley", "Watsonville", "Santa Cruz", "Half MoonBay", "Pacifica"};

int[][] SFKanW = {{ 0, 18, M, 12, 20, M, M, M, M, M, M, M, M, M, 15},{18, 0, 15, M, M, M, M, M, M, M, M, M, M, M, M},{ M, 15, 0, 15, M, M, M, M, M, M, M, M, M, M, M},{12, M, 15, 0, M, 20, M, M, M, M, M, M, M, M, M},{20, M, M, M, 0, 20, 18, M, M, M, M, M, M, 25, M},{ M, M, M, 20, 20, 0, M, 14, M, M, M, M, M, M, M},{ M, M, M, M, 18, M, 0, 15, M, 10, M, M, M, M, M},{ M, M, M, M, M, 14, 15, 0, 20, M, M, M, M, M, M},{ M, M, M, M, M, M, M, 20, 0, 15, M, 60, M, M, M},{ M, M, M, M, M, M, 10, M, 15, 0, 35, M, M, M, M},{ M, M, M, M, M, M, M, M, M, 35, 0, M, 10, M, M},{ M, M, M, M, M, M, M, M, 60, M, M, 0, 70, M, M},{ M, M, M, M, M, M, M, M, M, M, 10, 70, 0, 50, M},

{ M, M, M, M, 25, M, M, M, M, M, M, M, 50, 0, 15},{15, M, M, M, M, M, M, M, M, M, M, M, M, 15, 0}};

BGraph SFBayNetz = new BGraph (SFKn, SFKanW);

private static final int M = Integer.MAX_VALUE;

String[] SFKn = {"San Francisco", "San Rafael", "Richmond", "Oakland","San Mateo","Hayward", "Palo Alto", "Fremont", "San Jose", "SantaClara", "Scotts Valley", "Watsonville", "Santa Cruz", "Half MoonBay", "Pacifica"};

int[][] SFKanW = {{ 0, 18, M, 12, 20, M, M, M, M, M, M, M, M, M, 15},{18, 0, 15, M, M, M, M, M, M, M, M, M, M, M, M},{ M, 15, 0, 15, M, M, M, M, M, M, M, M, M, M, M},{12, M, 15, 0, M, 20, M, M, M, M, M, M, M, M, M},{20, M, M, M, 0, 20, 18, M, M, M, M, M, M, 25, M},{ M, M, M, 20, 20, 0, M, 14, M, M, M, M, M, M, M},{ M, M, M, M, 18, M, 0, 15, M, 10, M, M, M, M, M},{ M, M, M, M, M, 14, 15, 0, 20, M, M, M, M, M, M},{ M, M, M, M, M, M, M, 20, 0, 15, M, 60, M, M, M},{ M, M, M, M, M, M, 10, M, 15, 0, 35, M, M, M, M},{ M, M, M, M, M, M, M, M, M, 35, 0, M, 10, M, M},{ M, M, M, M, M, M, M, M, 60, M, M, 0, 70, M, M},{ M, M, M, M, M, M, M, M, M, M, 10, 70, 0, 50, M},

{ M, M, M, M, 25, M, M, M, M, M, M, M, 50, 0, 15},{15, M, M, M, M, M, M, M, M, M, M, M, M, 15, 0}};

BGraph SFBayNetz = new BGraph (SFKn, SFKanW);

Page 27: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.530 SFBay-Verkehrsnetz als ListenstrukturSan Francisco

118 1 12 3 20 4 15 14 nl

San Rafael2

18 0 15 2 nll

Richmond3

15 1 15 3 nl

Oakland4

12 0 15 2 20 5 nl

San Mateo5

20 0 20 5 18 6 25 13 nl

Hayward6

20 3 20 4 14 7 nl

Palo Alto7

18 4 15 7 10 9 nl

Fremont8

14 5 15 6 20 8 nl

San Jose9

20 7 15 9 60 11 nl

Santa Clara10

10 6 15 8 35 10 nl

Scotts Valley11

35 9 10 12 nl

Watsonville12

60 8 70 12 nl

Santa Cruz13

10 10 70 11 50 13 nl

Half Moon Bay14

25 4 50 12 15 14 nl

Pacifica 15 0 15 13 nl

0

nl kurz für : null

PInf II 9.540 Kürzester Weg: SFBay-Beispiel

Richmond

Oakland

Hayward

Fremont

San Jose

San Rafael

San Francisco

San Mateo

Palo Alto

15

1518

12

18

2020

20

10

15 20

14

12

0 3

5

74

68

10 Scotts Valley

12Santa Cruz

9Santa Clara

1513Half Moon Bay

11Watsonville

14Pacifica

15

15

25

50 35

10 70

60

Page 28: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.550 Der Weg von San Rafael nach Scotts Valley

Min-Suche Ausgangsknoten: San RafaelZielknoten: Scotts Valley

Die kürzeste Entfernung von San Rafaelnach Richmond ist 15 km.nach San Francisco ist 18 km.nach Oakland ist 30 km.nach Pacifica ist 33 km.nach San Mateo ist 38 km.nach Half Moon Bay ist 48 km.nach Hayward ist 50 km.nach Palo Alto ist 56 km.nach Fremont ist 64 km.nach Santa Clara ist 66 km.nach San Jose ist 81 km.nach Santa Cruz ist 98 km.nach Scotts Valley ist 101 km.----

Min-Suche Ausgangsknoten: San RafaelZielknoten: Scotts Valley

Die kürzeste Entfernung von San Rafaelnach Richmond ist 15 km.nach San Francisco ist 18 km.nach Oakland ist 30 km.nach Pacifica ist 33 km.nach San Mateo ist 38 km.nach Half Moon Bay ist 48 km.nach Hayward ist 50 km.nach Palo Alto ist 56 km.nach Fremont ist 64 km.nach Santa Clara ist 66 km.nach San Jose ist 81 km.nach Santa Cruz ist 98 km.nach Scotts Valley ist 101 km.----

PInf II 9.560 Traveling Salesman Problem

Ein Handlungsreisender muß eine Reihe von Orten besuchen. Er möchte seine Tour so planen, daß der zurückzulegende Weg (= Zyklus, der alle Orte berührt) minimal ist.

Ein Handlungsreisender muß eine Reihe von Orten besuchen. Er möchte seine Tour so planen, daß der zurückzulegende Weg (= Zyklus, der alle Orte berührt) minimal ist.

Dies ist ein Problem über bewerteten Graphen. Die Knoten sind die zu besuchen-den Orte, die (bewerteten) Kanten stellen die Entfernung (oder Fahrtkosten) zwischen den Orten dar. Das Graphenproblem lautet also :

Finde in einem vorgegebenen Graphen einen Zyklus minimaler Länge, der alle Knoten enthält.

Finde in einem vorgegebenen Graphen einen Zyklus minimaler Länge, der alle Knoten enthält.

Finden Sie eine TSP-Tour in dem nebenstehenden Graphen!

MR

GI

KS

MA

F

FD

KasselMarburg

Gießen

Frankfurt

Fulda

WürzburgMannheim

K

BN

Köln

Bonn

34

224 88 136

104

30

66

96

93181

104

174106

Page 29: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.570

Eigenschaften des TSPUnser Beispielproblem läßt sich offenbar intuitiv auf einfache Weise (und

eindeutig!) lösen. Grundsätzlich ist das jedoch keineswegs der Fall. (a) Oft gibt es überhaupt keine Lösung.

Beispiel: Wenn ein weiterer Knoten "Mainz" zwischen Bonn und Frankfurt aufgenommen wird, ist das TSP nicht lösbar.

(b) Oft gibt es mehrere Zyklen, die alle zu besuchenden Knoten enthalten. Dann kann der kürzeste Zyklus nur durch Ausprobieren gefunden werden. Beispiel: Wenn zusätzlich eineVerbindung Mainz-Mannheimaufgenommen wird, gibt es mehrere Zyklen.

MR

GI

KS

MA

F

FD

KasselMarburg

Gießen

Frankfurt

Fulda

WürzburgMannheim

K

BN

Köln

Bonn

34

224 88 136

104

30

66

96

93 140

104

174106

MZ41

71

PInf II 9.580

Lösungen des TSPBis heute ist keine effiziente Lösung des TSP bekannt. Alle bekannten Lösungen sind von der Art :

Allgemeiner TSP-Algorithmus:

• Erzeuge alle möglichen Touren;

• Berechne die Kosten für jede Tour;

• Wähle die kostengünstigsteTour aus.

Allgemeiner TSP-Algorithmus:

• Erzeuge alle möglichen Touren;

• Berechne die Kosten für jede Tour;

• Wähle die kostengünstigsteTour aus.

Folgerung : Die Komplexität aller bekannten TSP-Algorithmen ist O(cN), wobei N die Anzahl der Knoten und c die Maximalzahl der von einem Knoten ausgehenden Kanten ist. Das heißt: Wenn wir einen einzigen Knoten hinzunehmen, erhöht sich der Aufwand zur Lösung des TSP um den Faktor c!

Beispiel: Hinzunahme des neuen Knotens Mainz im obenstehenden Graphen.

Page 30: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.590

Varianten des TSPEine Verallgemeinerung des TSP besteht darin, auf die Rückkehr zum Ausgangspunkt zu verzichten:• TSP (u,v): Finde einen einfachen Weg von Knoten u nach Knoten v, der alle Knoten von G enthält. Offenbar stellt TSP(u,u) das ursprüngliche TSP-Problem dar. Beispiel: TSP(Marburg, Gießen) ist (im ursprünglichen Graphen) eindeutig

lösbar, TSP (Marburg, Köln) ist nicht lösbar, für TSP (Marburg, Würzburg) müssen im erweiterten Graphen mehrere mögliche Wege verglichen werden.

In einer weiteren Verallgemeinerung werden Mehrfachbesuche einzelner Knoten bis zu einer Schranke maxB (= Maximalzahl zulässiger Besuche) zugelassen.• TSP (u,v,maxB): Finde einen Weg von Knoten u nach Knoten v, der alle Knoten von G mindestens einmal und höchstens maxB-mal enthält.Mit dieser Verallgemeinerung (und geeignetem maxB) ist das TSP immer lösbar - allerdings mit noch erheblich gesteigerten Aufwand!

PInf II 9.600

TSP-Algorithmus: Erläuterung (1)Der folgende Algorithmus für das Problem TSP (u,v,maxB) verfolgt eine

Tiefensuche (depth first)- Strategie mit Zurücksetzen (backtracking). Er verwendet

• ein int-Array Marken der Länge knAnz zum Markieren der bereits besuchten Knoten,

• ein int-Array MinWeg der Länge maxKnBes zum Speichern des (bisher) minimalen Weges (mit maxKnBes = vorgegebene Schranke für die Anzahl zu besuchender Knoten),

• eine rekursive Prozedur Besuche, die einen begonnenen Weg der Tiefe t an der Stelle k mit dem Ziel z fortsetzt.Ist der Weg noch keine vollständige Tour, und existiert zu k ein Nachbarknoten k', der noch weniger als maxB-mal besucht wurde und bei dessen Besuch die gesamte Weglänge unterhalb der Weglängen-Schranke MinWegLen bleibt, wird der Weg bei k' mit Tiefe t+1fortgesetzt (rekursiver Aufruf).

Page 31: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.610

• Weiter wird durch eine boolesche Funktion TesteWeg geprüft, - ob k bereits der Zielknoten v ist,- ob alle Knoten besucht wurden.

• Ist ein Weg gefunden, so wird seine Länge (AktWegLen) mit der vonMinWeg (MinWegLen) verglichen. Falls er kürzer ist, wird er zum neuenMinWeg und seine Länge wird als neue MinWegLen festgehalten. Der Besuch wird abgebrochen.

• Beim Abbruch eines Besuchs wird zum vorherigen Knoten zurückgekehrt (backtrack) und der nächste mit diesem verbundene Knoten besucht. Wurden alle Wege durchprobiert und mindestens eine Tour gefunden, so wird MinWeg, ansonsten eine Fehlermeldung ausgegeben.

TSP-Algorithmus: Erläuterung (2)

PInf II 9.620

void besuche (int k, int [ ] weg, int tiefe, int [ ] marken, int ziel) {tiefe += 1; weg [tiefe] = k; marken[k] += 1; int bishWegLen = 0;if (tiefe == 0) aktWegLen =0 // Vorbesetzungelse {bishWegLen = aktWegLen;

aktWegLen += kantenW (weg[tiefe-1], k);}if (aktWegLen < minWegLen) // abbrechen, falls keine Verbesserung

if (testeWeg() { // Weg ist eine TSP-Tour) int [ ] minWeg = ... // Kopie von weg bis zur Position tiefe; minWegLen = aktWegLen; }

else // Weg ist keine TSP-Tourfor .. // alle Nachbarknoten j von k mit Marken [j] < maxB

besuche (j, weg, tiefe, marken, ziel); // in allen anderen Fällen Besuch zurücknehmen:

aktWegLen = bishWegLen; marken [k] -=1; }

void besuche (int k, int [ ] weg, int tiefe, int [ ] marken, int ziel) {tiefe += 1; weg [tiefe] = k; marken[k] += 1; int bishWegLen = 0;if (tiefe == 0) aktWegLen =0 // Vorbesetzungelse {bishWegLen = aktWegLen;

aktWegLen += kantenW (weg[tiefe-1], k);}if (aktWegLen < minWegLen) // abbrechen, falls keine Verbesserung

if (testeWeg() { // Weg ist eine TSP-Tour) int [ ] minWeg = ... // Kopie von weg bis zur Position tiefe; minWegLen = aktWegLen; }

else // Weg ist keine TSP-Tourfor .. // alle Nachbarknoten j von k mit Marken [j] < maxB

besuche (j, weg, tiefe, marken, ziel); // in allen anderen Fällen Besuch zurücknehmen:

aktWegLen = bishWegLen; marken [k] -=1; }

TSP-Algorithmus: Programmgerüst

Aufruf: besuche (u, weg, -1, marken, v)besuche (u, weg, -1, marken, v)

Page 32: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.630

class TSPGraph extends BGraph {

private int schranke ; // Abschätzung für Weglänge

private int minWegLen ; // Länge d.bish.minimalen Wegesprivate final int maxB = ...; /* Maximalzahl von Besuchen

pro Knoten, z.B. maxB = 2 */

private int aktWegLen; // Länge des aktuellen Weges

private int maxKnBes; /* Schranke für die Anzahlbesuchter Knoten */

private int[] minWeg; // Bisher minimaler Weg

void TSPSuche (int st, int zi) { // siehe nächste Folie} ...

}

class TSPGraph extends BGraph {

private int schranke ; // Abschätzung für Weglänge

private int minWegLen ; // Länge d.bish.minimalen Wegesprivate final int maxB = ...; /* Maximalzahl von Besuchen

pro Knoten, z.B. maxB = 2 */

private int aktWegLen; // Länge des aktuellen Weges

private int maxKnBes; /* Schranke für die Anzahlbesuchter Knoten */

private int[] minWeg; // Bisher minimaler Weg

void TSPSuche (int st, int zi) { // siehe nächste Folie} ...

}

Für die Implementierung des TSP-Algorithmus wird die Klasse Graph um zur Klasse TSPGraph erweitert. Dazu werden einige zusätzlich benötigte Datenfelder definiert. Die Operation TSPSuche ermittelt eine TSP-Tour. Als Hilfsroutinen benutzt sie die Operationen besuche und testeWeg.

Klassenerweiterung für TSP-Implementierung

PInf II 9.640

void TSPSuche (int st, int zi) { /* sucht minimale TSP-Tourvon st nach zi */

minWegLen = schranke; // Abschätzung nach oben

maxKnBes = ... ; /* geeigneter Wert > knAnz, z.B.(knAnz*maxB) +1 */

weg = new int[maxKnBes];marken = new int[knAnz];

for (int k = 0; k < knAnz; k++) marken[k] = 0;besuche (st, weg, -1, marken, zi);

if (minWegLen == schranke)System.out.println ("Kein Weg gefunden!");

else {System.out.println ("Länge des kürzesten Weges: " +minWegLen);System.out.println ("Besuchte Knoten: ");for (int k = 0; k < minWeg.length; k++)

System.out.println (knoBez[minWeg[k]]);}

}

void TSPSuche (int st, int zi) { /* sucht minimale TSP-Tourvon st nach zi */

minWegLen = schranke; // Abschätzung nach oben

maxKnBes = ... ; /* geeigneter Wert > knAnz, z.B.(knAnz*maxB) +1 */

weg = new int[maxKnBes];marken = new int[knAnz];

for (int k = 0; k < knAnz; k++) marken[k] = 0;besuche (st, weg, -1, marken, zi);

if (minWegLen == schranke)System.out.println ("Kein Weg gefunden!");

else {System.out.println ("Länge des kürzesten Weges: " +minWegLen);System.out.println ("Besuchte Knoten: ");for (int k = 0; k < minWeg.length; k++)

System.out.println (knoBez[minWeg[k]]);}

}

TSPSuche: Java-Programm

Page 33: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.650

private void besuche (int k, int[] weg, int tiefe, int[]marken, int zi) { /* besucht Knoten k mit geg. Weg,

Tiefe, Markierung und Ziel */tiefe ++;if (tiefe >= maxKnBes) {System.out.println ("Zulässige Knotenzahl im Wegüberschritten."); return; };

weg[tiefe] = k;marken[k] ++;int bishWegLen = 0;if (tiefe == 0) aktWegLen = 0; // Vorbesetzungelse {bishWegLen = aktWegLen;aktWegLen += kantenW (weg[tiefe-1], k);

}if (aktWegLen < minWegLen)if (testeWeg(weg, tiefe, marken, zi)) { // ist TSP-Tour

minWeg = new int[tiefe + 1];System.arraycopy (weg, 0, minWeg, 0, tiefe + 1) ;minWegLen = aktWegLen;

}else // siehe nächste Folie

private void besuche (int k, int[] weg, int tiefe, int[]marken, int zi) { /* besucht Knoten k mit geg. Weg,

Tiefe, Markierung und Ziel */tiefe ++;if (tiefe >= maxKnBes) {System.out.println ("Zulässige Knotenzahl im Wegüberschritten."); return; };

weg[tiefe] = k;marken[k] ++;int bishWegLen = 0;if (tiefe == 0) aktWegLen = 0; // Vorbesetzungelse {bishWegLen = aktWegLen;aktWegLen += kantenW (weg[tiefe-1], k);

}if (aktWegLen < minWegLen)if (testeWeg(weg, tiefe, marken, zi)) { // ist TSP-Tour

minWeg = new int[tiefe + 1];System.arraycopy (weg, 0, minWeg, 0, tiefe + 1) ;minWegLen = aktWegLen;

}else // siehe nächste Folie

Operation besuche

PInf II 9.660

else // Weg ist keine TSP-Tourfor (int j = 0; j < knAnz; j++)

//für alle Nachbarn j von k mit Marken[i] < maxBif (k != j && kantenW (k, j) < M)

if (marken[j] < maxB)besuche (j, weg, tiefe, marken, zi);

// in allen anderen Fällen Besuch zurücknehmen:aktWegLen = bishWegLen;weg[tiefe] = -1;marken[k] --;

}

private boolean testeWeg (int[] weg, int tiefe, int[]marken, int zi) { // prüft gegebenen Weg auf TSP-Tourif (tiefe < knAnz-1 |||||||| weg[tiefe] != zi) return false;for (int k = 0; k < knAnz; k++)if (marken[k] == 0) return false;

return true;}

else // Weg ist keine TSP-Tourfor (int j = 0; j < knAnz; j++)

//für alle Nachbarn j von k mit Marken[i] < maxBif (k != j && kantenW (k, j) < M)

if (marken[j] < maxB)besuche (j, weg, tiefe, marken, zi);

// in allen anderen Fällen Besuch zurücknehmen:aktWegLen = bishWegLen;weg[tiefe] = -1;marken[k] --;

}

private boolean testeWeg (int[] weg, int tiefe, int[]marken, int zi) { // prüft gegebenen Weg auf TSP-Tourif (tiefe < knAnz-1 |||||||| weg[tiefe] != zi) return false;for (int k = 0; k < knAnz; k++)if (marken[k] == 0) return false;

return true;}

Hilfsoperationen für TSP (Forts.)

Page 34: Kapitel 9: Graphen und Graph-Algorithmenertel/vorlesungen/ginf2/graphen-Kp09.pdf · Graphen: Begriffe und Definitionen Bewertete Graphen Graphen-Implementierungen Tiefen- und Breitensuche

PInf II 9.670 TSP von San Francisco nach San Rafael

Länge des kürzesten Weges: 342Besuchte Knoten:

San FranciscoPacificaHalf Moon BaySan MateoPalo AltoSanta ClaraScotts ValleySanta CruzWatsonvilleSan JoseFremontHaywardOaklandRichmondSan Rafael

Eine Lösung des TSP von San Francisco nachSan Rafael ist mit einem einfachen Weg möglich.

Laufzeit (auf 33MHZ 486 PC) < 1 sec

Eine Lösung des TSP von San Francisco nachSan Rafael ist mit einem einfachen Weg möglich.

Laufzeit (auf 33MHZ 486 PC) < 1 sec

PInf II 9.680 TSP von San Francisco nach San MateoLänge des kürzesten Weges: 364.Besuchte Knoten:

San FranciscoSan RafaelRichmondOaklandSan FranciscoPacificaHalf Moon BaySanta CruzScotts ValleySanta CruzWatsonvilleSan JoseSanta ClaraPalo AltoFremontHaywardSan Mateo

Eine Lösung des TSP von San Francisco nachSan Mateo gelingt nur mit Mehrfachbesuchen.

Laufzeit (auf 33MHZ 486 PC) ca. 200 sec

Eine Lösung des TSP von San Francisco nachSan Mateo gelingt nur mit Mehrfachbesuchen.

Laufzeit (auf 33MHZ 486 PC) ca. 200 sec