Suche in Graphen: BFS und DFS
1736 zeigte Leonhard Euler, dass kein Spaziergang jede der sieben Brücken von Königsberg genau einmal überquert, und mit dieser Antwort begann die Graphentheorie.
Punkte und Linien
Ein Graph ist eine Menge von Punkten, genannt Knoten (Ecken), die durch Linien verbunden sind, genannt Kanten. Die Idee ist schlicht und passt auf eine riesige Bandbreite von Dingen: Städte, verbunden durch Straßen, Webseiten, verbunden durch Links, Menschen, verbunden durch Freundschaften, Felder eines Labyrinths, verbunden, wo keine Wand ist. Ist ein Problem erst einmal als Graph gezeichnet, beantwortet dieselbe Handvoll Algorithmen Fragen wie „Komme ich von hier nach dort?“ und „Was ist der kürzeste Weg?“
Einen Graphen im Code speichern
Ein üblicher Weg, einen Graphen zu speichern, ist eine Adjazenzliste: zu jedem Knoten eine Liste seiner Nachbarn. Die Unterlagen des MIT-Kurses 6.006 verwenden genau das, ein Array oder Wörterbuch Adj, in dem Adj[u] die mit u verbundenen Knoten auflistet. Für den Graphen oben sieht das so aus:
const graph = {
A: ["B", "C"],
B: ["A", "D"],
C: ["A", "D", "E"],
D: ["B", "C", "F"],
E: ["C", "F"],
F: ["D", "E"],
};Breitensuche: Ring für Ring
Die Breitensuche (BFS, breadth-first search) erkundet einen Graphen in Ringen um den Start. Zuerst den Start selbst, Abstand 0. Dann alle seine Nachbarn, Abstand 1. Dann jeden neuen Knoten neben diesen, Abstand 2, und so weiter. Dafür hält sie eine Warteschlange (Queue), eine Reihe, in der das Erste, was hineinkommt, auch zuerst herauskommt. Nimm den Knoten vorn in der Warteschlange, besuch ihn und häng seine noch nicht gesehenen Nachbarn hinten an. Von A aus besucht die Breitensuche im Beispiel A, dann B und C, dann D und E, dann F. Weil sie sich Ring für Ring nach außen bewegt, hat sie beim ersten Erreichen eines Knotens einen Weg mit den wenigsten Kanten gefunden. F liegt 3 Kanten von A entfernt, zum Beispiel über A, B, D, F.
function bfs(graph, start) {
const seen = new Set([start]);
const queue = [start];
const order = [];
while (queue.length) {
const u = queue.shift(); // zuerst hinein, zuerst heraus
order.push(u);
for (const v of graph[u]) {
if (!seen.has(v)) { seen.add(v); queue.push(v); }
}
}
return order; // A B C D E F
}Tiefensuche: so weit wie möglich, dann zurück
Die Tiefensuche (DFS, depth-first search) verhält sich wie jemand im Labyrinth, der eine Hand an der Wand behält. Sie folgt einem Weg, so tief es geht. Kommt sie in eine Sackgasse oder an eine Stelle, die sie schon kennt, geht sie zur letzten Abzweigung zurück und probiert die nächste Möglichkeit. Sie verwendet statt einer Warteschlange einen Stapel, last in, first out, und lässt sich ganz natürlich mit Rekursion schreiben, wobei sich der Aufrufstapel alles merkt. Von A aus besucht eine rekursive Tiefensuche A, B, D, C, E, F. Die Tiefensuche erreicht jeden Knoten, den die Breitensuche erreicht, aber ihr Weg ist nicht unbedingt der kürzeste.
function dfs(graph, u, seen = new Set(), order = []) {
seen.add(u);
order.push(u);
for (const v of graph[u]) if (!seen.has(v)) dfs(graph, v, seen, order);
return order; // A B D C E F
}Zum Drehen ziehen. Zum Zoomen scrollen oder mit zwei Fingern ziehen.
Welche nimmt man?
Im ersten Labyrinth des Labors besucht die Breitensuche 105 Felder und findet einen Weg mit 22 Zügen, den kürzesten möglichen. Die Tiefensuche taucht zufällig in Richtung Ziel ab und besucht nur 31 Felder, aber ihr Weg braucht 28 Züge. Keine ist immer billiger. Beide behandeln jeden Knoten und jede Kante eine feste Zahl von Malen, also laufen beide in O(V + E) Zeit bei V Knoten und E Kanten. Nimm die Breitensuche, wenn du die wenigsten Schritte brauchst: die wenigsten Züge in einem Rätsel, die wenigsten Verbindungen zwischen zwei Menschen. Nimm die Tiefensuche, wenn du alles erkunden oder Entscheidungen bis zum Ende verfolgen musst: prüfen, ob alle Räume eines Labyrinths verbunden sind, oder die Züge eines Rätsels eine Linie nach der anderen ausprobieren und zurückgehen, wenn es nicht weitergeht. Haben Kanten verschiedene Längen, wie Straßen, reicht keine von beiden allein. Verfahren wie der Dijkstra-Algorithmus erweitern die Idee der Breitensuche um Entfernungen.
| Graph | Knoten, verbunden durch Kanten |
| Knoten (Ecke) | Ein Punkt eines Graphen |
| Kante | Eine Linie, die zwei Knoten verbindet |
| Adjazenzliste | Zu jedem Knoten die Liste seiner Nachbarn |
| Warteschlange (Queue) | First in, first out; von der Breitensuche verwendet |
| BFS | Breitensuche: Ring für Ring; findet Wege mit den wenigsten Kanten |
| DFS | Tiefensuche: erst in die Tiefe, dann zurück; verwendet einen Stapel |
| O(V + E) | Die Laufzeit von Breitensuche und Tiefensuche |
- Wie viele Kanten hat im Beispielgraphen der kürzeste Weg von A nach F?
- Wie viele Kanten hat der Beispielgraph?
- Welche Datenstruktur verwendet die Breitensuche?
- Eine Warteschlange (first in, first out)
- Einen Stapel (last in, first out)
- Eine sortierte Liste
- Gar keine Struktur
- Welche Suche findet garantiert einen Weg mit den wenigsten Kanten?
- BFS
- DFS
- Beide, immer
- Keine
- Bring die Knoten in die Reihenfolge, in der die Breitensuche von A aus sie besucht (Nachbarn in alphabetischer Reihenfolge).
- Bring die Knoten in die Reihenfolge, in der die rekursive Tiefensuche von A aus sie besucht (Nachbarn in alphabetischer Reihenfolge).
- Wie viele Züge hat auf dem 15 mal 9 Felder großen Gitter des Labors ohne Wände der kürzeste Weg von S (oben links) nach G (unten rechts)?
- Warum müssen sich Breitensuche und Tiefensuche merken, welche Knoten sie gesehen haben?
- Sonst würde ein Kreis im Graphen die Suche endlos laufen lassen
- Um sie am Ende auszugeben
- Um sie zu sortieren
- Nur die Tiefensuche braucht das
- In welchem Jahr löste Euler das Königsberger Brückenproblem?
- 1683
- 1736
- 1843
- 1945
- Welche Laufzeit hat die Breitensuche auf einem Graphen mit V Knoten und E Kanten?
- O(V + E)
- O(V × E)
- O(log V)
- O(2ⱽ)
- Du willst wissen, über wie wenige Freundesfreunde zwei Menschen in einem sozialen Netzwerk mindestens verbunden sind. Welche Suche?
- BFS
- DFS
- Binäre Suche
- Mergesort
- Ein Graph besteht aus Knoten, verbunden durch Kanten; Adjazenzlisten speichern ihn.
- Die Breitensuche verwendet eine Warteschlange, geht Ring für Ring vor und findet Wege mit den wenigsten Kanten.
- Die Tiefensuche verwendet einen Stapel (oder Rekursion), geht erst in die Tiefe und dann zurück.
- Beide laufen in O(V + E) und müssen sich gesehene Knoten merken.
- Eulers Lösung der Königsberger Brücken von 1736 begründete die Graphentheorie.
Quellen zu diesem Kurs (11)
- MIT OpenCourseWare 6.006 Introduction to Algorithms, Spring 2020 (Demaine, Ku, Solomon)
- lecture notes 1 Introduction, 3 Sorting, 9 Breadth-First Search, 10 Depth-First Search (ocw.mit.edu/courses/6-006-introduction-to-algorithms-spring-2020/pages/lecture-notes) and Fall 2011 lecture 13 BFS notes (ocw.mit.edu/courses/6-006-introduction-to-algorithms-fall-2011)
- Cormen, Leiserson, Rivest, Stein, Introduction to Algorithms (MIT Press)
- with the CLRS-based Khan Academy algorithms unit by Thomas Cormen and Devin Balkcom of Dartmouth (khanacademy.org/computing/computer-science/algorithms: binary search, selection sort analysis, merge sort, quicksort overview and analysis)
- MacTutor History of Mathematics, Al-Khwarizmi (mathshistory.st-andrews.ac.uk/Biographies/Al-Khwarizmi: word algorithm from the Latin title Algoritmi de numero Indorum)
- D. E. Joyce, Euclid's Elements Book VII Propositions 1 and 2 (mathcs.clarku.edu/~djoyce/elements/bookVII)
- Wolfram MathWorld, Merge Sort (mathworld.wolfram.com/MergeSort.html: proposed by John von Neumann in 1945, after Knuth 1998 p. 159)
- Stanford CS "Tony Hoare: Quicksort" (cs.stanford.edu/people/eroberts/courses/soco/projects/2008-09/tony-hoare/quicksort.html: devised while a student at Moscow State University for Russian-English machine translation with a tape dictionary, programmed in Algol 60 and published 1961; the year 1959 from the Wikipedia Quicksort article and its cited sources)
- Bodleian Libraries, Ada Lovelace (blogs.bodleian.ox.ac.uk/adalovelace: 1843 paper, Note G Bernoulli numbers)
- Fourmilab text of Menabrea's Sketch with Lovelace's notes (fourmilab.ch/babbage/sketch.html)
- Seven Bridges of Konigsberg (Euler 1736) and Tower of Hanoi (Edouard Lucas, 1883) checked against the Wikipedia articles and their cited sources.
Abgerufen: 2026-10-17