visited(v)

Werbung
FB Informatik
Prof. Dr. R.Nitsch
Programmieren 2
Future Car Projekt
Praktikum 6
- Speichern von Graphen
- Suchen in Graphen
- Kürzeste Wege
Reiner Nitsch
 [email protected]
Darstellung von Graphen als Array von Listen
• Grundlagen zu Graphen (siehe
Vorlesung Mathematik 1)
Array von
Adjazenzlisten
1
4
2
5
3
6
Ungerichteter Graph G(V,E)
V: Knotenmenge
E: Kantenmenge
1
2
4 
2
5
4
3
5
6 
4
1
2
5 
5
4
3
2 
6
3
6
1
2
2
5 
3
5
6
4
2 
Gerichteter Graph
5
4 
6
6 
1
4
2
5
09.10.2008
3
FB Informatik
Prof. Dr. R.Nitsch
4 
6 
Projekt FutureCar
Adjazenzlisten
1 
Die mit Knoten 5 verbunden
Nachbarknoten
(= Kantenmenge E5 )
Bietet Antwort auf Fragen zu Graphen
G wie z.B.
• Speicherbedarf?  proportional zur
Anzahl Knoten plus Anzahl Kanten 
O(|V|+|E|)
• Wieviele Kanten enden an vi?
• Welche Nachbarn vj hat vi?
• Existiert Kante E=(vi,vj)?  O(|Ei|)
Gewichteter Graph: Gewicht als zusätzliche Info der Listenelemente
2
Darstellung von Graphen mit Adjazenzmatrix
Adjazenzmatrix a mit Elementen aij
aij = 1 wenn Kante E=(vi,vj) in G
enthalten, sonst aij=0
1
2
3
4
5
6
Ungerichteter Graph G(V,E)
1
2
3
4
5
6
Gerichteter Graph G(V,E)
09.10.2008
0
1
2
i 3
4
5
0
0
1
0
1
0
0
1
1
0
0
1
1
0
j

2 3
0 1
0 1
0 0
0 0
1 1
1 0
4
0
1
1
1
0
0
5
0
0
1
0
0
1
 aij = aji (symmetrisch)
0
0
0
0
0
0
1
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
1
0
0
1
1
0
0
0
0
0
1
0
0
1
FB Informatik
Prof. Dr. R.Nitsch
Bietet Antwort auf Fragen zu
Graphen G wie z.B.
• Welche Kanten enden an vi?
• Welche Nachbarn vj hat vi?
• Existiert Kante E=(vi,vj)?
 O(1)
Speicherbedarf? O(|V|2)
 ungünstig wenn G wenige
Kanten hat
Gewichteter Graph: Gewicht an
Stelle von '1' in Matrix
eintragen
 aij ≠ aji (unsymmetrisch)
Projekt FutureCar
3
Adjazenzliste des Graphen der FutureCar World
FB Informatik
Prof. Dr. R.Nitsch
Rasterkarte (Grid) von FC-City
# # # #
#
1
4
#
#
#
x
y

0,0
1,0
2,0
3,0
0,1
1,1
2,1
3,1
0,2
1,2
2,2
3,2
0,3
1,3
2,3
3,3
Rasterkarte von FC-City
mit XY-Koordinaten
1,1
1,2 
2,1
1,1 
3,1
2,1
3,2 
1,2
2,2
1,3 
2,2
3,2
2,1 
3,2
3,1
4,2 
2
Adjazenzlisten
5
Graph zur Modellierung der
Erreichbarkeitsbeziehungen
zwischen den Zellen
der Rasterkarte
3
09.10.2008
Projekt FutureCar
11
21
31
12
22
32
12
11
21
22
32
31
32
13
21
42
Adjazenzlisteninfo
als Textsequenz 4
Implementierungsempfehlungen
1,1
FB Informatik
Prof. Dr. R.Nitsch
Node
1,2 
+ loc
+ adjList
2,1
1,1 
3,1
2,1
3,2 
1,2
2,2
1,3 
2,2
3,2
2,1 
3,2
3,1
4,2 
3,1
2,1 3,2
+ Konstruktor:
1..N
1
Graph
- nodes:Node[ ]
+ Graph(filename:string)
+…
Adjazenzlisten
Empfehlungen:
map<Location, Node> nodes
• Container zum Verwalten der Knoten?
• Knoten in Container einfügen?
nodes[ loc ] = Node(…)
• Was sollte in den Adjazenzlisten gespeichert werden? Location oder Adressen der Nachbarknoten.
• Welcher Datentyp eignet sich für Route?
Verwendbarkeit im Navi sicherstellen (Siehe Praktikum 5)
09.10.2008
Projekt FutureCar
5
Traversieren von Graphen
FB Informatik
Prof. Dr. R.Nitsch
• Als Traversieren bezeichnet man das systematische Besuchen aller Knoten und das
Durchlaufen jeder Kante eines Graphen.
• Algorithmen zum Traversieren eines Graphen dienen als Basis für viele andere
grundlegende Algorithmen zur Verarbeitung von Graphen
• Man unterscheidet zwischen
– Breitentraversierung (breadth-first search, BFS): Die Knoten werden geordnet
nach der "Entfernung" von einem Startknoten durchlaufen
• zuerst alle Knoten mit 1 Kantenlänge Abstand vom Startknoten
• danach alle diejenigen Knoten mit Abstand 2,
• danach die mit Abstand 3, usw.
– Tiefentraversierung (depth-first search, DFS): Dieser Algorithmus erhöht
immer zuerst die Distanz vom Startknoten, bevor er in die Breite geht und
Nachbarknoten mit gleicher Distanz besucht (meist rekursiv implementiert)
• Bereits besuchte Knoten müssen markiert werden,
Node
weil sich die Algorithmen sonst in den Kreisen des
+ loc:Location
Graphen verlieren.
Markierung
+ adjList
+ visited:bool
+ Konstruktor:
09.10.2008
Projekt FutureCar
6
Beispiel zur Tiefentraversierung
FB Informatik
Prof. Dr. R.Nitsch
procedure traverse-dfs(v)
visited(v) := true
vnext := adjList[v]
WHILE( exist(vnext)
UND NOT visited(vnext)
traverse-dfs(vnext)
vnext := succ(v_next)
END WHILE
Adjazenzlisten
von Seite 2
Startknoten
2
3
1
2
3
1
2
3
4
5
6
4
5
6
4
5
6
1
2
3
4
09.10.2008
2
5
2
4 
2
5
4
3
5
6 
4
1
2
5 
5
4
3
2 
6
3
6
1 
Komplexität: O(|V|+|E|)
1
1
1
3
6
4
5
6
Projekt FutureCar
Alle Knoten und
Kanten besucht!
9
Tiefentraversierung (Iterativ)
FB Informatik
Prof. Dr. R.Nitsch
• Iterativer Algorithmus mit einem Stack, der ausgehend von einer unmarkierten Ecke
vi, alle anderen Knoten vj, j!=i eines Graphen G (genauer: einer Komponente
desselben) besucht
PRE: ---
POST: Alle Ecken, die von v erreichbar sind, sind markiert.
procedure traverse-dfs(v)
t := empty-stack
visited(v) := true
push(t,v)
WHILE NOT empty(t) DO {
v := top(t)
vnext := adjList[v]
// t ist ein lokaler Stack
// markiere v als besucht
// lege v auf den Stack
// hole oberstes Element aus Stack
// hole ersten Nachbarknoten
WHILE exist(vnext) AND visited(vnext) DO // schon besucht?
vnext := succ(vnext)
// Ja! Dann eben den Nächsten
END WHILE
IF exist(vnext) DO
// Noch einen Unbesuchten gefunden?
visit(vnext)
// diesen besuchen (und bearbeiten),
visited(vnext) := true
// als "besucht" markieren und
push(t,vnext)
// Erst mal auf den Stack damit ...
ELSE DO
pop(t)
END IF
END WHILE
// Erledigt! Alle Nachbarn von v besucht
}
09.10.2008
Projekt FutureCar
… und hier
schon wieder
runter!
10
Breitentraversierung
FB Informatik
Prof. Dr. R.Nitsch
• Iterativer Algorithmus, der alle Knoten eines zusammenhängenden Graphen geordnet nach der
Entfernung vom Startknoten v durchläuft.
– Zuerst werden alle vom Startknoten über 1 Kante erreichbaren Knoten besucht
– Danach alle über mindestens 2 Kanten erreichbaren Knoten, usw.
– Entsteht formal aus Tiefentraversierung, wenn man den Stack durch eine Queue ersetzt.
PRE:
--Post:
Alle Knoten, die von v erreichbar sind, sind markiert, also besucht worden
procedure bfs_node(v)
t := empty-queue
// Definition einer leeren lokalen Queue
visited(v) := true
// Starknoten v als "besucht" markieren
enqueue(t,v)
Die Änderungen gegenüber
WHILE NOT empty(t) DO
der Tiefensuche sind ROT
v := front(t)
// vordersten Knoten in t lesen
markiert!
vnext := adjList[v]
// hole ersten Nachbarknoten
WHILE exist(vnext) AND visited(vnext) DO
// schon besucht?
vnext := succ(vnext)
// Ja! Dann eben den Nächsten
END WHILE
IF exist(vnext) DO
// Noch einen Unbesuchten gefunden?
visit(vnext)
// diesen besuchen (und bearbeiten),
visited(vnext) := true
// als "besucht" markieren und
enqueue(t,v_next)
// erst mal in queue einreihen, wo sie bis zur
// Bearbeitung ihrer Nachbarknoten warten
ELSE DO
dequeue(t)
// Erledigt! Alle Nachbarn von v wurden besucht
END IF
11
Projekt FutureCar
END09.10.2008
WHLE
Beispiel zur Breitentraversierung
FB Informatik
Prof. Dr. R.Nitsch
procedure bfs_node(v)
t := empty-queue
visited(v) := true
enqueue(t,v)
WHILE NOT empty(t) DO
v := front(t)
vnext := adj[v]
WHILE exist(vnext) AND visited(vnext) DO
vnext := succ(vnext)
IF exist(vnext) DO
visit(v_next)
visited(vnext) := true
enqueue(t,vnext)
ELSE
2
t:
dequeue(t) END IF END WHILE
Startknoten
Adjazenzlisten
von Seite 2
5
4
1
3
3
1
2
3
1
2
3
4
5
6
4
5
6
4
5
6
1
2
3
1
2
3
09.10.2008
6
4
5
4 
2
5
4
3
5
6 
4
1
2
5 
5
4
3
2 
6
3
6
1 
6
2
5
2
Queue
1
4
1
6
Projekt FutureCar
Jetzt sind alle
Knoten mit Distanz
"1Kante" zum
Startknoten besucht
Alle Knoten und
Kanten besucht!
12
Kürzeste Wege mittels Breitensuche
FB Informatik
Prof. Dr. R.Nitsch
• Gesucht ist eine Verbindung (Pfad) zwischen 2 Knoten:
– Tiefensuche liefert eine entsprechende Kantenfolge, wenn es eine gibt (aber nicht
unbedingt die Kürzeste).
– Breitensuche liefert garantiert die Kürzeste.
• Aufgabe
Mit Hilfe eines Breitensuchverfahrens soll der kürzeste Weg in einem ungewichteten
Graphen G vom Startpunkt src zum Zielknoten dest gefunden werden, der über die
geringste Anzahl von Kanten verläuft. Dabei wird der Weg so codiert, dass man ihn
hinterher rekonstruieren kann.
• Lösung
– Die Breitentraversierung durchläuft alle Knoten geordnet nach der Kantendistanz
zu src.
– Der Vorgängerknoten, von dem ausgehend der Knoten v betreten wird, verbindet
somit v auf dem kürzesten Wege mit src (keine Kantengewichte!).
– Im Bearbeitungsschritt merkt sich Knoten v daher seinen Vorgängerknoten
– Nachdem Knoten dest betreten wurde und dieser sich seinen Vorgängerknoten
gemerkt hat, ist die Suche beendet.
– Der kürzeste Weg, der dest mit src verbindet, ergibt sich nun, indem man,
beginnend bei dest, die Folge der Vorgängerknoten rekonstruiert.
– Rückwärts gelesen (std::reverse) ergibt diese Folge den gesuchten kürzesten
Weg.
13
09.10.2008
Projekt FutureCar
Breitensuche des Knotens d ausgehend vom Startknoten s
PRE:
POST:
FB Informatik
Prof. Dr. R.Nitsch
exist(s), exist(d)
Alle Knoten, die von v erreichbar sind, sind markiert, also besucht worden
procedure bf_search(s,d)
t := empty-queue
visited(s) := true
pred(s) := nil
enqueue(t,s)
Ergänzungen zum vorherigen Algorithmus sind ROT markiert
// Definition einer leeren lokalen Queue
// Starknoten v als "besucht" markieren
// s kennt seinen Vorgänger noch nicht
Verweis auf Vorgängerknoten
Node
+ loc:Location
+ adjList
+ pred
+ visited:bool
WHILE NOT empty(t) AND front(t)!=d DO // Abbruch der Suche wenn d besucht
+ Konstruktor:
v := front(t)
// vordersten Knoten in t lesen
vnext := adj[v]
// hole ersten Nachbarknoten
WHILE exist(v_next) AND visited(v_next) DO
vnext := succ(vnext)
// Bereits besuchte Knoten überspringen
END WHILE
IF vnext != nil DO
// Solange unbesuchte Nachbarknoten zu v existieren
visit(vnext)
// diese besuchen (und bearbeiten),
pred(vnext) := v
// Vorgänger merken (besuchen & bearbeiten)
visited(vnext) := true
// als solche markieren und
enqueue(t,vnext)
// in queue einfügen, wo sie bis zur Bearbeitung
// ihrer Nachbarknoten warten
ELSE DO
dequeue(t)
// entferne vorderstes Element aus t
END IF
// Alle Nachbarn dieses Knotens sind besucht
END WHILE
IF empty(t) DO { kein Pfad von s nach d} ELSE DO reverse(t) END IF
09.10.2008
Projekt FutureCar
14
Herunterladen