
Inhaltsverzeichnis
Was ist Dijkstras Algorithmus?
Dijkstras Algorithmus, 1959 von Edsger W. Dijkstra veröffentlicht, findet den kürzesten Weg von einer einzigen Quelle zu jedem anderen Knoten in einem gewichteten Graphen. Er funktioniert sowohl bei gerichteten als auch bei ungerichteten Graphen, mit einer festen Bedingung: jedes Kantengewicht muss nichtnegativ sein.
Stellen Sie sich den Graphen als Straßennetz vor. Knoten sind Kreuzungen, Kanten sind Straßen, und jedes Gewicht ist die Zeit oder Entfernung, um diese Straße zu befahren. Dijkstras Algorithmus beantwortet die Frage, die jede Navigations-App stellt: Was ist die schnellste Route von meinem Standort zu allen anderen? Er ist ein Eintrag in der größeren Familie, die in Algorithmen für kürzeste Wege behandelt wird, und fester Bestandteil des Graphentheorie-Lernpfads.
Die Grundidee
Dijkstras Algorithmus ist gierig. Er hält für jeden Knoten eine vorläufige kürzeste Distanz und wiederholt einen einfachen Schritt:
Besuche stets den unbesuchten Knoten mit der kleinsten bekannten Distanz und nutze ihn, um seine Nachbarn zu verbessern.
Die Einsicht, die das korrekt macht: Weil alle Gewichte nichtnegativ sind, kann kein zukünftiger Weg den nächstgelegenen unbesuchten Knoten günstiger erreichen, sobald man ihn gewählt hat. In dem Moment, in dem ein Knoten gewählt wird, ist seine Distanz also endgültig. Diese eine Garantie ist der ganze Algorithmus.
Konkret verwaltet der Algorithmus:
- Eine Distanz zu jedem Knoten, alle beginnen bei unendlich, außer der Quelle, die
0ist. - Eine Prioritätswarteschlange (Min-Heap), die stets den nächstgelegenen unbesuchten Knoten zurückgibt.
- Eine Menge endgültiger Knoten, deren kürzeste Distanz feststeht.
Das Verbessern eines Nachbarn heißt Relaxation: Ist der Weg über den aktuellen Knoten kürzer als die gespeicherte Distanz des Nachbarn, senkt man sie.
So funktioniert er, Schritt für Schritt
Lassen wir Dijkstra vom Knoten A auf diesem gewichteten Graphen laufen. Die blauen Kanten bilden den endgültigen Kürzeste-Wege-Baum, und die Zahl neben jedem Knoten ist seine endgültige kürzeste Distanz von A.
Hier ist der Ablauf. In jedem Schritt legen wir den nächstgelegenen unbesuchten Knoten endgültig fest (fett) und relaxieren seine Nachbarn. ∞ bedeutet "noch nicht erreicht".
| Besuch | A | B | C | D | E | F |
|---|---|---|---|---|---|---|
| Start | 0 | ∞ | ∞ | ∞ | ∞ | ∞ |
| A (0) | 0 | 4 | 2 | ∞ | ∞ | ∞ |
| C (2) | 0 | 3 | 2 | 10 | ∞ | ∞ |
| B (3) | 0 | 3 | 2 | 8 | ∞ | ∞ |
| D (8) | 0 | 3 | 2 | 8 | 10 | 14 |
| E (10) | 0 | 3 | 2 | 8 | 10 | 13 |
| F (13) | 0 | 3 | 2 | 8 | 10 | 13 |
Beachten Sie Schritt drei: Der Besuch von C senkte B von 4 auf 3, weil die Route A → C → B (2 + 1) die direkte Kante A → B (4) schlägt. Das ist Relaxation bei der Arbeit. Dies auf einem Live-Graphen ablaufen zu sehen, macht das Muster sofort klar, was Sie im Algorithmen-Visualisierer tun können.
Implementierung in Python
Die saubere, interviewreife Version verwendet Pythons heapq als Prioritätswarteschlange. Der Graph ist eine Adjazenzliste, die jeden Knoten auf eine Liste von (Nachbar, Gewicht)-Paaren abbildet.
import heapq
def dijkstra(graph, start):
# Jeder Knoten beginnt unendlich weit entfernt, außer der Quelle.
distances = {node: float('inf') for node in graph}
distances[start] = 0
pq = [(0, start)] # (bisherige Distanz, Knoten)
while pq:
dist, node = heapq.heappop(pq)
# Ein veralteter, längerer Eintrag für einen bereits festgelegten Knoten: überspringen.
if dist > distances[node]:
continue
for neighbour, weight in graph[node]:
new_dist = dist + weight
# Relaxation: einen günstigeren Weg zum Nachbarn gefunden.
if new_dist < distances[neighbour]:
distances[neighbour] = new_dist
heapq.heappush(pq, (new_dist, neighbour))
return distances
Zwei Details sind wichtig. Erstens fügen wir einen neuen Eintrag hinzu, statt den Heap direkt zu aktualisieren, und überspringen dann veraltete Einträge mit der Prüfung dist > distances[node]. Dieses "faule Löschen" hält den Code einfach und ist gängige Praxis. Zweitens berechnet der Algorithmus von Natur aus Distanzen zu allen Knoten; um bei einem einzigen Ziel früh zu stoppen, geben Sie zurück, sobald Sie es entnehmen.
Zeit- und Speicherkomplexität
Die Kosten hängen von der Prioritätswarteschlange ab. Jede Kante kann höchstens ein Einfügen auslösen, und jedes Einfügen oder Entnehmen bei einem binären Heap kostet O(log V).
| Prioritätswarteschlange | Zeit | Am besten wenn |
|---|---|---|
| Binärer Heap | O((V + E) log V) | Die übliche Wahl, dünn besetzte Graphen |
| Fibonacci-Heap | O(E + V log V) | Dichte Graphen, theoretisch bestes |
| Einfaches Array | O(V²) | Sehr dichte Graphen |
Der Speicher ist O(V) für die Distanztabelle plus die Warteschlange. Für die Begründung dieser Schranken und ihren Vergleich über alle Graphenalgorithmen hinweg siehe den Leitfaden zur Komplexität von Graphenalgorithmen und den einseitigen Spickzettel.
Wann Dijkstra versagt: negative Gewichte
Die gierige Garantie beruht ganz auf nichtnegativen Gewichten. Fügen Sie eine negative Kante hinzu, und das Ganze kann zusammenbrechen.
Angenommen, der Algorithmus legt einen Knoten endgültig fest, weil er am nächsten scheint, Distanz 5. Später entdeckt er eine länger wirkende Route, die über eine -4-Kante führt und diesen Knoten tatsächlich in 3 erreicht. Zu spät: Dijkstra hat 5 bereits für endgültig erklärt und ist weitergezogen. Die Antwort ist falsch.
Merkregel: nichtnegative Gewichte, nimm Dijkstra. Jedes negative Gewicht, nimm Bellman-Ford, der jede Kante wiederholt relaxiert und auch negative Zyklen erkennen kann.
Dijkstra im Vergleich zu anderen Algorithmen
Dijkstra ist eines von mehreren Werkzeugen. Das richtige auszuwählen, hängt vom Graphen ab.
| Algorithmus | Negative Gewichte? | Am besten für | Zeit |
|---|---|---|---|
| BFS | Nur ungewichtet | Kürzester Weg, ungewichtet | O(V + E) |
| Dijkstra | Nein | Nichtnegative Gewichte | O((V + E) log V) |
| Bellman-Ford | Ja | Negative Gewichte, Zyklusprüfung | O(V · E) |
| A* | Nein | Ein Ziel, mit Heuristik | O(E) typisch |
Der nächste Verwandte ist die A*-Suche, also Dijkstra plus eine Heuristik, die die Suche auf ein einziges Ziel lenkt. Und Dijkstra selbst ist eigentlich Breitensuche, aufgerüstet von einer einfachen Warteschlange zu einer Prioritätswarteschlange.
Anwendungen in der Praxis
- Navigation und Karten: die kürzeste oder schnellste Route zwischen zwei Orten, der klassische Einsatz.
- Netzwerk-Routing: Link-State-Protokolle wie OSPF nutzen Dijkstra, um Weiterleitungstabellen zu berechnen.
- Spiele und Robotik: Bewegungskosten über eine Karte, oft mit A* darauf aufgesetzt.
- Betrieb und Logistik: kostenminimale Wege in Liefer-, Telekom- und Verkehrsnetzen, ein Grundpfeiler des Operations Research.
Sehen Sie zu, wie Dijkstra seinen nächsten Knoten wählt
Die Prioritätswarteschlange wird in dem Moment klar, in dem Sie sehen, wie sie immer wieder den günstigsten Knoten zieht. Lassen Sie Dijkstra Schritt für Schritt auf einem Live-Graphen laufen.
Algorithmen-Visualisierer öffnenHäufig gestellte Fragen
Was macht Dijkstras Algorithmus?
Dijkstras Algorithmus findet den kürzesten Weg von einem einzelnen Startknoten zu jedem anderen Knoten in einem gewichteten Graphen, solange alle Kantengewichte nichtnegativ sind. Er ist die Standardmethode hinter Routing, Kartendiensten und Netzwerkprotokollen.
Wie hoch ist die Zeitkomplexität von Dijkstras Algorithmus?
Mit einem binären Heap als Prioritätswarteschlange läuft Dijkstras Algorithmus in O((V + E) log V) Zeit und O(V) Speicher. Mit einem Fibonacci-Heap verbessert er sich auf O(E + V log V), und mit einem einfachen Array ist er O(V hoch 2), was bei dichten Graphen schneller ist.
Warum funktioniert Dijkstras Algorithmus nicht mit negativen Gewichten?
Dijkstra legt jeden Knoten endgültig fest, sobald er aus der Prioritätswarteschlange entfernt wird, und nimmt an, dass später kein günstigerer Weg auftauchen kann. Eine negative Kante bricht diese Annahme, weil eine längere Route die Gesamtkosten dennoch senken könnte. Verwenden Sie Bellman-Ford für Graphen mit negativen Gewichten.
Ist Dijkstras Algorithmus dasselbe wie BFS?
Dijkstra verallgemeinert die Breitensuche. BFS verwendet eine einfache Warteschlange und findet den kürzesten Weg in ungewichteten Graphen. Dijkstra ersetzt die Warteschlange durch eine Min-Prioritätswarteschlange, sodass er stets den nächstgelegenen unbesuchten Knoten expandiert und gewichtete Kanten verarbeitet.