Breitensuche

Werbung
Breitensuche
Warteschlangen und die Grundidee der Breitensuche
Im Folgenden wollen wir Graphen systematisch von einem Startknoten aus durchmustern, das
heißt, alle Knoten sollen besucht und alle Kanten berührt werden, wobei nach dem Startknoten
s erst alle Nachbarn von s, dann alle Nachbarn der Nachbarn usw. besucht werden sollen.
Dazu sei G = (V, E) ein ungerichteter Graph gegeben durch seine Adjazenzlistendarstellung
und s ∈ V ein fest gewählter Startknoten.
Wir werden den Knoten ‘Farben’ geben welche deren aktuellen Zustand symbolisieren:
• weiß: Knoten wurde noch nicht gesehen; zu Beginn sind alle Knoten weiß
• grau: Knoten wurde schon gesehen, wir müssen aber noch überprüfen, ob er noch weiße
Nachbarn hat
• schwarz: Knoten ist erledigt, Knoten selbst und alle seine Nachbarn wurden gesehen.
Die noch zu untersuchenden grauen Knoten werden in einer Warteschlange Q verwaltet. Als
Datenstruktur ist eine Warteschlange dadurch charakterisiert, dass Objekte in einer linearen
Ordnung gehalten werden, neue Objekte kann man nur am Ende einfügen und zugreifen bzw.
entfernen kann man nur den Kopf der Schlange, also das Objekt, was am längsten in der
Schlange war. Eine solche Struktur realisiert das sogenannte FIFO-Prinzip : First–In–First–
Out.
Ohne die Details der Implementierung einer Warteschlange zu spezifizieren, werden wir ihre
Funktionalität im Pseudocode verwenden, insbesondere die Funktion enqueue v in Q zum
Einfügen des Knoten v in die Warteschlange Q und die Funktion dequeue Q um den vordersten
Knoten aus der Warteschlange zu entfernen.
Ein Knoten, dessen Farbe von weiß nach grau wechselt, wird ans Ende der Schlange eingefügt.
Die Schlange wird vom Kopf her abgearbeitet. Ist die Schlange leer, sind alle von s erreichbaren
Knoten erledigt.
Mit BFS kann der Abstand d[u] = dG (s, u) eines erreichbaren Knotens u von s berechnet
werden (Beweis später). Intuitiv sollte dies aber klar sein, denn der Algorithmus besucht
zunächst alle Knoten im Abstand i von s, danach erst alle im Abstand i + 1. Gleichzeitig
wird ein Baum von kürzesten Wegen von s zu allen erreichbaren Knoten aufgebaut. Dieser ist
dadurch beschrieben, dass man für jeden Knoten u einen Zeiger π[u] auf den Vorgängerknoten
auf einem kürzesten Weg von s nach u aufrechterhält. Ist dieser noch nicht bekannt oder
existiert gar nicht, so ist der Zeiger auf N IL gesetzt.
Pseudocode und ein Beispiel
Breitensuche BFS(G,s):
for all u ∈ V(G) \{ s}
Farbe[u] = weiss
d[u] = ∞
π[u] = NIL
1
Farbe[s] = grau
d[s] = 0
π[s] = NIL
enqueue s in Q
while Q not empty
u = dequeue Q
for all v ∈ Adj[u]
if Farbe[v] = weiss then
Farbe[v] = grau
d[v] = d[u]+1
π[v] = u
enqueue v in Q
Farbe[u] = schwarz
Beispiel: Im Beispiel werden die Adjazenzlisten als lexikographisch sortiert angenommen.
s
a
b
c
8
8
8
0
Zustand nach Initialisierung:
Nur der Startknoten s ist grau
(dargestellt als gepunkteter Kreis)
8
e
8
8
d
f
Inhalt der Warteschlange:
a
b
c
8
1
j
8
0
i
8
h
8
s
8
8
g
Q=[s]
Zustand nach Entfernung von s aus
der Warteschlange Q
f
schwarze Knoten und Baumkanten
8
e
8
d 1
sind durch dicke Linien dargestellt
Inhalt der Warteschlange:
a
0
1
d 1
e 3
g 2
3 h
i
8
h
8
s
8
8
g
b
c
2
3
Endzustand wenn Q leer ist:
BFS−Baum dick gezeichnet
f
3
i
Q=[a,d]
j
4
4
j
2
Die Laufzeit des BFS–Algorithmus ist offensichtlich O(|V | + |E|). Man beachte, dass der konstruierte Baum kürzester Wege von der Reihenfolge der Knoten in den Adjazenzlisten abhängt,
der Abstand der Knoten selbst natürlich nicht. Genauer, verschiedene Adjazenzlistendarstellungen ein und desselben Graphen können nichtisomorphe Breitensuchbäume produzieren.
Ist der Graph G ungerichtet und zusammenhängend, so liefert BFS einen aufspannenden
Baum. Hat G mehrere Zusammenhangskomponenten, so kann man leicht einen aufspannenden
Wald erzeugen. Man startet die BFS–Suche erneut bei einem beliebigen noch weißen Knoten.
Was kann man damit noch anfangen? Auch der Test, ob ein ungerichteter Graph bipartit (also
2-färbbar) ist, wird jetzt sehr einfach. Wir konstruieren einen aufspannenden Wald, färben
dessen Knoten mit zwei Farben und testen abschließend, ob die Graphkanten, die nicht im
Wald sind, korrekt 2–gefärbt sind.
Breitensuche und der Abstand vom Startknoten
Wir wollen jetzt zeigen, dass man mit BFS korrekt die graphentheoretischen Abstände in G
vom Startknoten s berechnet, also für alle Knoten v gilt: d[v] = dG (s, v).
Beweis der BFS–Korrektheit:
Wir führen einen Induktionsbeweis über den Zeitpunkt der Aufnahme des Knoten v in die
Warteschlange Q. Um alle Voraussetzungen zur Ableitung der Induktionsbehauptung zur
Verfügung zu haben, wird die zu beweisende Aussage wie folgt erweitert:
1. d[v] ≥ dG (s, v)
2. Ist w das (aktuell) erste und v das letzte Element in Q, dann gilt d[v] − d[w] ≤ 1
3. Wird v nach x in Q aufgenommen, dann gilt d[x] ≤ d[v], d.h. die Folge der d[.]-Werte
in Q ist schwach monoton wachsend.
4. Ist dG (s, x) < dG (s, v), dann wird x vor v in Q aufgenommen.
5. d[v] = dG (s, v)
Induktionsanfang: s wird als erster Knoten in Q aufgenommen, laut Initialisierung mit d[s] = 0
und somit sind alle Bedingungen erfüllt. Danach wird s als Kopfelement aus Q entfernt und
alle Nachbarn v von s mit d[v] = 1 in Q eingefügt. Auch dabei sind alle Bedingungen erfüllt.
Induktionvoraussetzung: Die Bedingungen 1 bis 5 gelten für alle Knoten v 0 , die vor v in Q
eingefügt wurden.
Induktionsschritt: Wir betrachten den Zeitpunkt der Aufnahme von v in die Warteschlange. Sei u der Knoten, der zur Aufnahme von v in Q führt (d.h. u ist der zuletzt entfernte
Kopfknoten und v ein weißer Nachbar von u).
(1) Offensichtlich muss dG (s, u) + 1 ≥ dG (s, v) sein. Nach Induktionsvoraussetzung ist d[u] =
dG (s, u) und folglich d[v] = d[u] + 1 ≥ dG (s, v).
(2) Ist w der aktuelle Kopfknoten in Q, dann ist nach Induktionsvoraussetzung (Bedingung
3) d[u] ≤ d[w] und folglich d[v] − d[w] = d[u] + 1 − d[w] ≤ 1.
3
(3) Da sowohl vor der Entfernung von u aus Q als auch vor der Aufnahme von v in Q die
Voraussetzungen 2 und 3 erfüllt waren, gilt wegen d[v] = d[u] + 1 Aussage 3 auch nach
Aufnahme von v.
(4) Sei x ein Knoten mit dG (s, x) < dG (s, v). Nehmen wir an, dass x nicht vor v in der
Warteschlange war. Offensichtlich muss x 6= s sein. Sei y der Vorgänger von x auf einem
kürzesten Weg von s nach x. Dann ist dG (s, y) = dG (s, x) − 1 < dG (s, v) − 1 ≤ dG (s, u). Nach
Induktionsvoraussetzung (bezüglich u) muss dann y vor u in Q gewesen sein. Spätestens bei
der Entfernung von y aus Q wären alle (noch) weißen Nachbarn von y in die Warteschlange
gelangt, also x vor v - Widerspruch.
(5) Sei z der Vorgänger von v auf einem tatsächlich kürzestem Weg von s nach v.
Fall 1: z wurde vor u in Q aufgenommen. Dann wäre v (spätestens) schon beim Entfernen
von z aus Q in die Warteschlange gelangt - Widerspruch!
Fall 2: z = u oder z wurde nach u in Q aufgenommen. Dann ist nach Induktionsvoraussetzung dG (s, u) = d[u] ≤ d[z] = dG (s, z) und folglich d[v] = d[u] + 1 ≤ dG (z) + 1 ≤ dG (s, v).
Zusammen mit der Bedingung 1 ergibt sich die Gleichheit.
Bemerkung: Der eigentliche, tiefere Grund, weshalb dieser Beweis funktioniert, ist die banale Feststellung, dass ein kürzester Weg (hier von s zu v) als Teilwege wieder kürzeste Wege
enthält (hier von s nach z). Dies werden wir bei anderen Kürzeste–Wege–Problemen wiederfinden.
4
Herunterladen