4.6 KiB
title | date |
---|---|
Breitensuche | 2018-11-13 |
(ungerichtete Graphen)
Frage: Gegeben ein Graph G = (V,E)
und s \in V
. Wie finden wir die kürzesten Pfade zum Knoten
u \in V \setminus \{s\}
? Ohne Einschränkung V = \{1,...,n\}
.
Zuerst:
a) Speichern von Graphen
Man möchte unter Anderem folgendes berechnen/testen:
i) für $u,v \in V$ ist $\{u,v\} \in E$?
ii) $\Gamma(u) = \{v \in V \mid \{u,v\} \in E\}$
Der Aufwand für diese Operationen ist abhängig davon, wie wir den Graphen speichern.
1. Möglichkeit:
Adjazenzmatrix $A$ mit $A_{ij} = \begin{cases}
1 & \{i,j\} \in E \\
0 & \text{sonst}
\end{cases}$
Speicheraufwand: $O(n^2)$
* testen ob $\{u,v\} \in E$ kostet $O(1)$ (teste ob $A_{ij} = 1$ oder nicht)
* berechnen von $\Gamma(u)$ kostet $O(n)$
1. Möglichkeit:
Verkettete Listen (Adjazenzlisten)
Für jeden Knoten $u \in \{1,...,n\}$ speichern wir eine (aufsteigend sortierte) Liste der Nachbarn von $u$.
*Beispiel*:
![Graph](20181113_2-adjlist.png)
| Knoten | Nachbarn |
| --- | --- |
| 1 | 2, 3, 4 |
| 2 | 1, 4 |
| 3 | 1 |
| 4 | 1, 2 |
| 5 | 6 |
| 6 | 5 |
Speicherplatz: $\sum\limits_{u \in V} | \Gamma(u) | = \sum\limits_{u \in V} deg(u) = 2 |E| \Rightarrow O(n+|E|)
= O(|V| + |E|)$
* testen ob $\{u,v\} \in E$ kostet $O(min\{deg(u), deg(v)\})$
* berechnen von $\Gamma(u)$. Nachbarschaft entspricht genau der Adjazenzliste. $O(|\Gamma(u)|) = O(deg(u))$
b) Warteschlange
*Beispiel*:
i) Supermarkt
ii) Kommunikationskanal
**Abstraktion**: Sei $U$ eine endliche Menge. Eine Queue ist eine Datenstruktur mit folgenden Operationen:
i) Erzeugen einer leeren Warteschlange `Q <- new Queue()`
ii) Einfügen von Elementen in die Warteschlange `Q.enqueue(u)` mit $u \in U$
iii) Entfernen des *zuerst* hinzugefügten Elements aus der Warteschlange `u <- Q.dequeue()`
iv) Testen, ob die Warteschlange leer ist `Q.isEmpty()`
Queue ist eine `FIFO` Datenstruktur
Damit können wir die Breitensuche beschreiben.
Algorithmus (Breitensuche)
Eingabe: Graph G = (V,E)
mit V = \{1,...,n\}
als Adjazenzliste und sei s \in V
i) für alle v \in V\setminus\{s\}
setze d[v] =
\infty
und pred[v] = NIL
i) d[s] = 0
i) Q <- new Queue()
i) Q.enqueue(s)
i) while (!Q.isEmpty())
a) v <- Q.dequeue()
a) Für alle $u \in \Gamma(v)$
Falls d[u] =
\infty
dann
* setze d[u] = d[v] + 1
* setze pred[u] = v
* Q.enqueue(u)
i) Ausgabe: d[u]
, pred[u]
für alle u \in V
Beispiel:
Startknoten: s = 1
Queue |
---|
1 |
2, 4, 3 |
4, 3, 5 |
3, 5 |
5 |
v | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
d[v] |
0 | 1 | 1 | 1 | 2 |
pred[v] |
1 | 1 | 1 | 2 |
Satz (Breitensuche)
Sie Breitensuche liefert für den Startknoten s
die kürzesten $u$-$s$-Pfade für alle u \in V
als p = (u, pred[u], pred[pred[u]], ..., s)
in Laufzeit O(|V| + |E|)
Beweis (Breitensuche)
i) Laufzeit ist klar
i) Korrektheit
Für u \in V
ist p = (u, pred[u], pred[pred[u]], ..., s)
ein Pfad in G
(da jeder Knoten maximal einmal in die
Queue eingefügt wird, gibt es keine Knotenwiederholung). Die Länge von p
ist d[u]
, da
* d[u] - 1 = d[pred[u]]
* d[pred[pred[u]]] = d[pred[u]] - 1 = d[u] - 2
usw. bis `d[s] = 0`.
Sei $p' = (v_0 = u, v_1, ... v_k = s)$ ein weiterer $u$-$s$-Pfad. Zu zeigen: `d[u]` $\leq$ `k`. Es gilt für alle
$\{u',v'\} \in E$
```
d[v'] <= d[u'] + 1
```
$\Rightarrow d[u] \leq d[v_1] + 1 \leq d[v_2] + 2 \leq ... \leq d[v_k] + k \leq d[s] + k = k$
$$
\tag*{$\Box$}
$$
Satz (Breitensuche \to
Spannbaum)
Breitensuche liefert bei Eingabe eines zusammenhängenden Graph G = (V,E)
und s \in V
einen Spannbaum T=(V,E')
mit
E' = \{\{u, pred[u]\} \mid u \in V \setminus \{s\}\}
von G
.
Beweis (Breitensuche \to
Spannbaum)
Zu zeigen: T
ist Spannbaum von G
, das heißt
i) T
ist zusammenhängend
Für $u,v \in V$ sind
* $p = (u, pred[u], ..., s$
* $p' = (v, pred[v], ..., s$
Pfade in $T$. Damit ist $u \sim v$ und $v \sim u$ in $T$ und damit (Transitivität) $u \sim v$ in T
$\Rightarrow T$ ist zusammenhängend
i) T
ist kreisfrei
$T$ ist kreisfrei, da $T$ zusammenhängend und $|E'| = |V| + 1$ (siehe Übung)
\tag*{$\Box$}