Grundlagen

Dijkstra vs Bellman-Ford vs Floyd-Warshall

Dijkstra, Bellman-Ford und Floyd-Warshall finden alle kürzeste Wege, beantworten aber unterschiedliche Fragen unter unterschiedlichen Bedingungen. Dieser Leitfaden lässt alle drei auf einem Beispielgraphen mit einer negativen Kante laufen, zeigt genau, warum Dijkstra daran scheitert, erklärt, wie die anderen beiden negative Zyklen erkennen, vergleicht ihre Geschwindigkeit anhand gemessener Operationszahlen und behandelt den Johnson-Algorithmus sowie die Sonderfälle, in denen eine einfachere Methode gewinnt, durchgehend mit getestetem Python-Code.

22 Min Lesezeit Aktualisiert: Oktober 2026 Anfänger
Mohammed Islam Hadjoudj
Mohammed Islam Hadjoudj
Expert Operations Research Engineer

1. Die kurze Antwort

Der Algorithmus von Dijkstra, der Bellman-Ford-Algorithmus und der Floyd-Warshall-Algorithmus finden alle kürzeste Wege in einem gewichteten Graphen, beantworten aber unterschiedliche Fragen unter unterschiedlichen Bedingungen. Zwei Fragen entscheiden zwischen ihnen: Brauchen Sie Distanzen von einer Quelle aus oder zwischen allen Paaren von Knoten? Und kann eine Kante ein negatives Gewicht haben?

DijkstraBellman-FordFloyd-Warshall
BeantwortetVon einer Quelle zu jedem KnotenVon einer Quelle zu jedem KnotenJedes Knotenpaar
Negative KantengewichteNein: liefert falsche AntwortenJaJa
Erkennt negative ZyklenNeinJaJa
ZeitO((V + E) log V) mit binärem HeapO(V E)O(V3)
SpeicherO(V)O(V)O(V2)
StrategieGreedy: den nächstgelegenen Knoten abschließenWiederholen: jede Kante relaxierenDynamische Programmierung über Zwischenstationen
Einsetzen, wenndie Gewichte nichtnegativ sind, was die meisten Straßen-, Netzwerk- und Spielkarten abdecktmanche Gewichte negativ sind oder ein negativer Zyklus erkannt werden mussSie alle Distanzen in einem kleinen oder dichten Graphen brauchen

Hier ist V die Anzahl der Knoten und E die Anzahl der Kanten. In einem Satz: Verwenden Sie Dijkstra standardmäßig, wechseln Sie zu Bellman-Ford, wenn Gewichte negativ sein können, und verwenden Sie Floyd-Warshall, wenn Sie die gesamte Distanztabelle eines Graphen mit höchstens einigen hundert Knoten brauchen. Wenn Sie alle Paare in einem großen dünnen Graphen mit negativen Kanten brauchen, kombiniert der Johnson-Algorithmus in Abschnitt 7 die ersten beiden. Der Rest dieses Leitfadens lässt alle drei auf demselben Graphen laufen und zeigt, wo jeder gewinnt und wo jeder scheitert.

Ein Entscheidungsdiagramm. Erste Frage: Brauchen Sie alle Paare oder eine Quelle? Bei einer Quelle: Können Gewichte negativ sein? Wenn nein, Dijkstra; wenn ja, Bellman-Ford. Bei allen Paaren: Können Gewichte negativ sein, und ist der Graph dicht? Dichte oder kleine Graphen verwenden Floyd-Warshall; dünne Graphen verwenden Dijkstra von jedem Knoten aus, wenn die Gewichte nichtnegativ sind, und den Johnson-Algorithmus, wenn nicht. Randnotizen: Ungewichtete Graphen brauchen nur eine Breitensuche, und azyklische Graphen lassen sich in linearer Zeit in topologischer Reihenfolge lösen.
Zwei Fragen bestimmen den Algorithmus: eine Quelle oder alle Paare, und ob negative Gewichte vorkommen können. Die Dichte entscheidet zwischen den Optionen für alle Paare.

2. Ein Graph, drei Antworten

Das durchgehende Beispiel ist ein kleiner gerichteter Graph mit sechs Knoten, sieben Kanten und einer negativen Kante, B → A mit Gewicht -4. Stellen Sie sich die Gewichte als Kosten vor: Negative Kosten sind eine Rückerstattung, etwa eine Mautstraße, die Sie dafür bezahlt, dass Sie sie benutzen, oder ein Schritt in einem chemischen Prozess, der Energie freisetzt.

S → A  3      S → B  6      A → C  4      B → A  -4
C → T  2      B → D  3      D → T  4

6 Knoten, 7 Kanten, keine Zyklen; Quelle S

Die günstigste Route von S nach T ist S, B, A, C, T, mit Kosten 6 - 4 + 4 + 2 = 8. Sie beginnt mit dem teureren ersten Schritt nach B und holt ihn über die Rückerstattung wieder herein. Das folgende Programm implementiert die drei Algorithmen in ihrer üblichen Lehrbuchform und startet sie alle im Knoten S.

import heapq
INF = float('inf')

def graph(edges):
    adj = {}
    for u, v, w in edges:
        adj.setdefault(u, []).append((v, w))
        adj.setdefault(v, [])
    return adj

def dijkstra(adj, s):
    """Greedy: den nächstgelegenen offenen Knoten abschließen, nie wieder besuchen."""
    dist = {v: INF for v in adj}
    dist[s] = 0
    done, heap = set(), [(0, s)]
    while heap:
        d, u = heapq.heappop(heap)
        if u in done:
            continue                      # veralteter Heap-Eintrag
        done.add(u)
        for v, w in adj[u]:
            if v not in done and d + w < dist[v]:
                dist[v] = d + w
                heapq.heappush(heap, (dist[v], v))
    return dist

def bellman_ford(adj, s):
    """Jede Kante relaxieren, Runde um Runde, bis sich nichts mehr ändert."""
    dist = {v: INF for v in adj}
    dist[s] = 0
    for _ in range(len(adj) - 1):
        changed = False
        for u in adj:
            for v, w in adj[u]:
                if dist[u] + w < dist[v]:
                    dist[v] = dist[u] + w
                    changed = True
        if not changed:
            break
    return dist

def floyd_warshall(adj):
    """Alle Paare: die Knoten einzeln nacheinander als Zwischenstationen zulassen."""
    d = {u: {v: 0 if u == v else INF for v in adj} for u in adj}
    for u in adj:
        for v, w in adj[u]:
            d[u][v] = min(d[u][v], w)
    for k in adj:
        for i in adj:
            for j in adj:
                if d[i][k] + d[k][j] < d[i][j]:
                    d[i][j] = d[i][k] + d[k][j]
    return d

G = graph([('S', 'A', 3), ('S', 'B', 6), ('A', 'C', 4), ('B', 'A', -4),
           ('C', 'T', 2), ('B', 'D', 3), ('D', 'T', 4)])
dj, bf, fw = dijkstra(G, 'S'), bellman_ford(G, 'S'), floyd_warshall(G)['S']
for v in G:
    print(v, dj[v], bf[v], fw[v], '' if dj[v] == bf[v] else '<- Dijkstra is wrong')
# S 0 0 0
# A 3 2 2 <- Dijkstra is wrong
# B 6 6 6
# C 7 6 6 <- Dijkstra is wrong
# T 9 8 8 <- Dijkstra is wrong
# D 9 9 9
Links: der Beispielgraph mit den Knoten S, A, B, C, D und T, die negative Kante von B nach A rot eingezeichnet und die kürzeste Route S, B, A, C, T grün. Rechts: eine Tabelle der Distanzen von S aus. Die wahren Distanzen sind S 0, A 2, B 6, C 6, D 9 und T 8. Bellman-Ford und Floyd-Warshall stimmen mit allen überein. Dijkstra meldet A 3, C 7 und T 9, drei falsche Werte, rot dargestellt.
Auf einem Graphen mit einer negativen Kante liegt Dijkstra bei drei von sechs Distanzen falsch. Bellman-Ford und Floyd-Warshall stimmen mit den wahren Werten überein.

Bellman-Ford und Floyd-Warshall liefern die wahren Distanzen. Dijkstra meldet 3 für A, 7 für C und 9 für T, alle zu hoch, und gibt dabei keinen Fehler aus: Die falschen Antworten sehen genau wie richtige aus. Abschnitt 4 erklärt, warum. Auf einem Graphen mit nichtnegativen Gewichten würden alle drei übereinstimmen, und der einzige Unterschied wäre die Geschwindigkeit.

3. Was jeder Algorithmus tut

Alle drei Algorithmen bauen auf einer einzigen Operation auf, der Relaxation. Jeder Knoten v trägt eine vorläufige Distanz dist[v], die bei unendlich beginnt, bei der Quelle bei 0. Die Relaxation der Kante u → v mit Gewicht w prüft, ob der Weg über u günstiger ist, und hält ihn in diesem Fall fest:

if dist[u] + w < dist[v]:
    dist[v] = dist[u] + w

Eine vorläufige Distanz sinkt immer nur, und sie entspricht stets den Kosten einer tatsächlich existierenden Route. Die Algorithmen unterscheiden sich nur darin, welche Kanten sie relaxieren und in welcher Reihenfolge.

Links: der Lauf von Dijkstra auf dem Beispiel als Zeitleiste. Er schließt S bei 0 ab, dann A bei 3, das für endgültig erklärt wird; als B bei 6 abgeschlossen wird, kommt die günstigere Route über B nach A mit Kosten 2 zu spät, also werden C bei 7 und T bei 9 abgeschlossen. Rechts: die Runden von Bellman-Ford als Tabelle. Runde 1 ergibt A 2, B 6, C 7, D 9 und T 9; Runde 2 senkt C auf 6 und T auf 8; Runde 3 ändert nichts, also hält der Algorithmus an.
Dijkstra legt sich bei A auf Kosten 3 fest, bevor er die Rückerstattung gesehen hat. Bellman-Ford relaxiert so lange jede Kante, bis eine ganze Runde nichts mehr ändert.

Im Beispiel besucht Bellman-Ford die Kanten in der Reihenfolge, in der sie aufgelistet wurden. Schon seine erste Runde senkt A auf 2, wenn sie die Kanten erreicht, die von B ausgehen, aber bis dahin hat sie den alten Wert 3 bereits benutzt, um C = 7 zu setzen. Die zweite Runde trägt die Verbesserung weiter zu C = 6 und T = 8. Die dritte Runde ändert nichts, also hält der Algorithmus vorzeitig an, statt alle V - 1 = 5 Runden zu durchlaufen. Wie viele Runden nötig sind, hängt von der Kantenreihenfolge ab, überschreitet aber nie V - 1.

4. Warum Dijkstra an negativen Kanten scheitert

Die Geschwindigkeit von Dijkstra beruht auf einem Versprechen: Wenn ein Knoten die Prioritätswarteschlange verlässt, ist seine Distanz endgültig. Das Versprechen stützt sich auf ein Argument, das nichtnegative Gewichte braucht. Angenommen, u ist der nächstgelegene offene Knoten. Jede andere Route zu u muss den abgeschlossenen Bereich über einen offenen Knoten x verlassen, der bereits mindestens so weit entfernt ist wie u, und der Rest der Route kann nur Kosten hinzufügen. Mit einer negativen Kante kann der Rest der Route Kosten abziehen, und das Argument bricht zusammen.

Genau das passiert im Beispiel. Dijkstra schließt S bei 0 ab, dann A bei 3, weil 3 kleiner ist als die 6 von B. Dann wird B abgeschlossen, und seine Kante nach A bietet 6 - 4 = 2, A ist jedoch bereits endgültig, und die Verbesserung wird verworfen. Alles, was auf A aufbaut, erbt den Fehler: C wird bei 3 + 4 = 7 statt 6 abgeschlossen und T bei 9 statt 8.

Zwei naheliegende Korrekturen funktionieren nicht:

Mit nichtnegativen Gewichten tritt nichts davon auf, und Dijkstra ist korrekt und der schnellste der drei. Gewichte von null sind kein Problem: Das Argument braucht nur, dass keine Kante die Kosten senkt.

5. Negative Zyklen

Ein negativer Zyklus ist ein Zyklus, dessen Gewichte sich zu weniger als null summieren. Ist er von der Quelle aus erreichbar und kann er das Ziel erreichen, gibt es überhaupt keine kürzeste Route: Eine weitere Runde durch den Zyklus ist immer günstiger. Die Distanz ist minus unendlich, und ein Algorithmus sollte den Zyklus melden statt einer Zahl. Fügen Sie die Kante C → B mit Gewicht -5 zum Beispiel hinzu, und der Zyklus B, A, C, B kostet -4 + 4 - 5 = -5.

INF = float('inf')

def graph(edges):
    adj = {}
    for u, v, w in edges:
        adj.setdefault(u, []).append((v, w))
        adj.setdefault(v, [])
    return adj

def negative_cycle(adj):
    """Bellman-Ford von einer virtuellen Quelle aus, die mit Kosten 0 mit jedem Knoten verbunden ist.
    Gibt einen negativen Zyklus als Liste von Knoten zurück, sonst None."""
    dist = {v: 0 for v in adj}
    parent = {v: None for v in adj}
    for _ in range(len(adj)):                  # eine Runde mehr als üblich
        last = None
        for u in adj:
            for v, w in adj[u]:
                if dist[u] + w < dist[v]:
                    dist[v], parent[v], last = dist[u] + w, u, v
        if last is None:
            return None                        # eine ruhige Runde: kein negativer Zyklus
    for _ in range(len(adj)):                  # weit genug zurückgehen, um auf dem Zyklus zu landen
        last = parent[last]
    cycle, v = [last], parent[last]
    while v != last:                           # den Vorgängern einmal rund um den Zyklus folgen
        cycle.append(v)
        v = parent[v]
    cycle.reverse()                            # Vorgänger zeigen rückwärts
    return cycle + cycle[:1]

def floyd_warshall_diagonal(adj):
    """Knoten, deren Distanz zu sich selbst negativ ausfällt."""
    d = {u: {v: 0 if u == v else INF for v in adj} for u in adj}
    for u in adj:
        for v, w in adj[u]:
            d[u][v] = min(d[u][v], w)
    for k in adj:
        for i in adj:
            for j in adj:
                if d[i][k] + d[k][j] < d[i][j]:
                    d[i][j] = d[i][k] + d[k][j]
    return [v for v in adj if d[v][v] < 0]

edges = [('S', 'A', 3), ('S', 'B', 6), ('A', 'C', 4), ('B', 'A', -4),
         ('C', 'T', 2), ('B', 'D', 3), ('D', 'T', 4)]
print(negative_cycle(graph(edges)))
# None
bad = graph(edges + [('C', 'B', -5)])          # B -> A -> C -> B kostet -4 + 4 - 5 = -5
print(negative_cycle(bad))
# ['C', 'B', 'A', 'C']
print(floyd_warshall_diagonal(bad))
# ['A', 'B', 'C']

Die Funktion startet Bellman-Ford von einer virtuellen Quelle aus, die mit Kosten 0 mit jedem Knoten verbunden ist, und findet so einen negativen Zyklus irgendwo im Graphen, nicht nur einen, der von S aus erreichbar ist. Im ursprünglichen Beispiel ändert irgendwann eine Runde nichts mehr, und sie gibt None zurück. Mit der zusätzlichen Kante gibt sie den Zyklus C, B, A, C zurück, denselben Zyklus von einem anderen Start aus geschrieben, und Floyd-Warshall markiert A, B und C auf seiner Diagonale. In Tests mit Tausenden zufälliger Graphen markierte die Diagonale genau dann einen negativen Zyklus, wenn einer existierte, markierte aber nicht immer jeden Knoten, der einen erreichen kann. Verwenden Sie sie also als Ja-Nein-Test.

Eine Falle erwischt viele. In einem ungerichteten Graphen kann eine Kante in beide Richtungen begangen werden, also ist schon eine einzelne Kante mit negativem Gewicht ein negativer Zyklus: u, v, u. Bellman-Ford auf der gerichteten Version eines ungerichteten Graphen meldet daher einen negativen Zyklus, sobald irgendeine Kante negativ ist, eine Falle, die Sedgewick und Wayne in Algorithms ausdrücklich beschreiben. Kürzeste einfache Wege mit negativen Gewichten in ungerichteten Graphen erfordern andere, aufwendigere Methoden.

6. Geschwindigkeit: welcher schneller ist und wann

Für eine einzelne Quelle ist die Reihenfolge klar, wann immer Dijkstra anwendbar ist: O((V + E) log V) mit binärem Heap gegenüber O(V E) bei Bellman-Ford. Auf einem Graphen mit 10.000 Knoten und 50.000 Kanten sind das grob eine Million Heap-Schritte gegenüber bis zu 500 Millionen Kantenrelaxationen. Floyd-Warshall ist kein Verfahren für eine einzelne Quelle, und wer es für eine Quelle laufen lässt, verschwendet fast seine gesamte Arbeit.

Für alle Paare ist der Vergleich knapper, denn die Alternativen bestehen darin, ein Verfahren für eine einzelne Quelle (Single-Source) von jedem Knoten aus zu starten:

Verfahren für alle PaareZeitNegative KantenAm besten für
Dijkstra von jedem Knoten ausO(V (V + E) log V)NeinDünne Graphen, nichtnegative Gewichte
Bellman-Ford von jedem Knoten ausO(V2 E)JaFast nie
Floyd-WarshallO(V3)JaDichte oder kleine Graphen
Johnson-AlgorithmusO(V E + V (V + E) log V)JaDünne Graphen mit negativen Kanten

Asymptotische Schranken verbergen Konstanten, deshalb zählt das folgende Programm die elementaren Schritte, die jedes Verfahren tatsächlich ausführt, auf zwei zufälligen gerichteten Graphen mit 100 Knoten: einem dünnen mit 300 Kanten und einem dichten mit 4.950, der Hälfte aller möglichen Kanten. Bei Dijkstra zählt es Kantenprüfungen und Heap-Operationen getrennt, da eine Heap-Operation etwa log V Schritte kostet.

import heapq, random
INF = float('inf')

def random_graph(n, m, seed):
    """n Knoten, m verschiedene gerichtete Kanten mit Gewichten 1..100."""
    rnd = random.Random(seed)
    pairs = rnd.sample([(u, v) for u in range(n) for v in range(n) if u != v], m)
    adj = {v: [] for v in range(n)}
    for u, v in pairs:
        adj[u].append((v, rnd.randint(1, 100)))
    return adj

def all_pairs_dijkstra(adj):
    """Dijkstra von jedem Knoten aus. Zählt Kantenprüfungen und Heap-Operationen."""
    checks = heap_ops = 0
    for s in adj:
        dist, heap = {v: INF for v in adj}, [(0, s)]
        dist[s] = 0
        while heap:
            d, u = heapq.heappop(heap)
            heap_ops += 1
            if d > dist[u]:
                continue
            for v, w in adj[u]:
                checks += 1
                if d + w < dist[v]:
                    dist[v] = d + w
                    heapq.heappush(heap, (dist[v], v))
                    heap_ops += 1
    return checks, heap_ops

def all_pairs_bellman_ford(adj):
    """Bellman-Ford von jedem Knoten aus, mit vorzeitigem Abbruch, wenn eine Runde nichts ändert."""
    m, checks = sum(len(a) for a in adj.values()), 0
    for s in adj:
        dist = {v: INF for v in adj}
        dist[s] = 0
        for _ in range(len(adj) - 1):
            changed = False
            for u in adj:
                for v, w in adj[u]:
                    if dist[u] + w < dist[v]:
                        dist[v], changed = dist[u] + w, True
            checks += m
            if not changed:
                break
    return checks

n = 100
for name, m in [('sparse', 3 * n), ('dense', n * (n - 1) // 2)]:
    adj = random_graph(n, m, seed=1)
    checks, heap_ops = all_pairs_dijkstra(adj)
    print(f'{name:6} E={m:4}  Dijkstra x V: {checks:7,} checks + {heap_ops:6,} heap ops   '
          f'Bellman-Ford x V: {all_pairs_bellman_ford(adj):9,}   Floyd-Warshall: {n ** 3:,}')
# sparse E= 300  Dijkstra x V:  27,565 checks + 21,388 heap ops   Bellman-Ford x V:   226,200   Floyd-Warshall: 1,000,000
# dense  E=4950  Dijkstra x V: 495,000 checks + 64,770 heap ops   Bellman-Ford x V: 2,712,600   Floyd-Warshall: 1,000,000
Ein Balkendiagramm mit logarithmischer Skala der Schritte, die nötig sind, um die Distanzen aller Paare auf Zufallsgraphen mit 100 Knoten zu berechnen. Auf dem dünnen Graphen mit 300 Kanten: Dijkstra von jedem Knoten aus etwa 49.000 Schritte, Bellman-Ford von jedem Knoten aus 226.200, Floyd-Warshall 1.000.000. Auf dem dichten Graphen mit 4.950 Kanten: Dijkstra von jedem Knoten aus etwa 560.000, Bellman-Ford 2.712.600, Floyd-Warshall 1.000.000.
Aufwand für alle Paare bei 100 Knoten. Auf dem dünnen Graphen ist Dijkstra von jedem Knoten aus etwa 20-mal günstiger als Floyd-Warshall; auf dem dichten Graphen liegen beide innerhalb eines Faktors zwei.

Auf dem dünnen Graphen braucht Dijkstra von jedem Knoten aus etwa 49.000 Schritte gegenüber den festen 1.000.000 von Floyd-Warshall, ein Faktor von etwa 20. Auf dem dichten Graphen schließt sich die Lücke fast, und sobald die logarithmischen Kosten pro Heap-Operation mitgezählt werden, gewinnt in der Praxis oft die einfache innere Schleife von Floyd-Warshall, die den Speicher der Reihe nach durchläuft und keine Prioritätswarteschlange braucht. Bellman-Ford von jedem Knoten aus ist in beiden Fällen am langsamsten, selbst mit vorzeitigem Abbruch. Die Faustregel: Unter einigen hundert Knoten oder wenn die meisten Knotenpaare durch eine Kante verbunden sind, verwenden Sie Floyd-Warshall wegen seiner Einfachheit; auf großen dünnen Graphen verwenden Sie Dijkstra oder Johnson von jedem Knoten aus.

7. Alle Paare mit negativen Kanten: der Johnson-Algorithmus

Donald Johnsons Algorithmus von 1977 vereint das Beste aus beiden. Er lässt Bellman-Ford einmal laufen, um für jeden Knoten eine Zahl h(v) zu berechnen, die Distanz von einer virtuellen Quelle aus, die mit Kosten 0 mit allen Knoten verbunden ist. Dann ersetzt er jedes Gewicht durch

w'(u, v) = w(u, v) + h(u) - h(v)

Weil h die Bedingung h(v) ≤ h(u) + w(u, v) für jede Kante erfüllt, ist jedes neue Gewicht nichtnegativ. Und entlang jeder Route von s nach t heben sich die Terme teleskopartig auf, sodass sich die Kosten jeder Route um denselben Betrag ändern, h(s) - h(t). Kürzeste Routen bleiben kürzeste, und Dijkstra kann nun von jedem Knoten aus laufen. Anders als das Addieren einer Konstanten hängt diese Umgewichtung nicht davon ab, wie viele Kanten eine Route hat.

import heapq
INF = float('inf')

def graph(edges):
    adj = {}
    for u, v, w in edges:
        adj.setdefault(u, []).append((v, w))
        adj.setdefault(v, [])
    return adj

def dijkstra(adj, s):
    dist, heap = {v: INF for v in adj}, [(0, s)]
    dist[s] = 0
    while heap:
        d, u = heapq.heappop(heap)
        if d > dist[u]:
            continue
        for v, w in adj[u]:
            if d + w < dist[v]:
                dist[v] = d + w
                heapq.heappush(heap, (dist[v], v))
    return dist

def johnson(adj):
    """Alle Paare mit negativen Kanten: einmal Bellman-Ford, dann Dijkstra von jedem Knoten aus."""
    h = {v: 0 for v in adj}                    # Distanzen von einer virtuellen Quelle aus
    for _ in range(len(adj)):
        for u in adj:
            for v, w in adj[u]:
                h[v] = min(h[v], h[u] + w)
    for u in adj:
        for v, w in adj[u]:
            if h[u] + w < h[v]:
                raise ValueError('negative cycle')
    # umgewichten: jede Kante wird nichtnegativ, jeder Weg ändert sich um h[s] - h[t]
    lifted = {u: [(v, w + h[u] - h[v]) for v, w in adj[u]] for u in adj}
    return {s: {t: d - h[s] + h[t] for t, d in dijkstra(lifted, s).items()} for s in adj}, lifted

G = graph([('S', 'A', 3), ('S', 'B', 6), ('A', 'C', 4), ('B', 'A', -4),
           ('C', 'T', 2), ('B', 'D', 3), ('D', 'T', 4)])
dist, lifted = johnson(G)
print({u: [(v, w) for v, w in lifted[u]] for u in ('S', 'A', 'B')})
# {'S': [('A', 7), ('B', 6)], 'A': [('C', 0)], 'B': [('A', 0), ('D', 3)]}
print(dist['S'])
# {'S': 0, 'A': 2, 'B': 6, 'C': 6, 'T': 8, 'D': 9}
print(dist['B'])
# {'S': inf, 'A': -4, 'B': 0, 'C': 0, 'T': 2, 'D': 3}

Im Beispiel gilt h(A) = -4, und jeder andere Knoten erhält 0. Die negative Kante B → A wird zu 0, S → A steigt von 3 auf 7 und A → C fällt von 4 auf 0, und Dijkstra auf den neuen Gewichten, zurückkorrigiert um h, liefert die wahren Distanzen von jeder Quelle aus, einschließlich der Zeile für B mit ihrem negativen Eintrag -4. Findet Bellman-Ford im ersten Schritt einen negativen Zyklus, hält der Johnson-Algorithmus an und meldet ihn.

8. Wenn keiner der drei die beste Wahl ist

Manche Graphen haben eine Struktur, die eine einfachere oder schnellere Methode ausnutzen kann:

9. Speicher und das Rekonstruieren der Wege

Distanzen reichen oft nicht: Sie wollen die Route selbst. Dijkstra und Bellman-Ford speichern einen Vorgänger pro Knoten, den Knoten, von dem die beste Route kam, und die Route nach t wird rückwärts ab t gelesen. Das kostet O(V) zusätzlichen Speicher. Floyd-Warshall braucht neben seiner Distanztabelle eine vollständige V mal V große Tabelle mit Einträgen für den „nächsten Hop“, also liegt sein Speicherbedarf bei O(V2). Bei 10.000 Knoten mit 8-Byte-Einträgen belegt allein die Distanztabelle etwa 800 Megabyte, und die Tabelle der nächsten Hops braucht noch einmal genauso viel. Meist ist es der Speicher, mehr als die Zeit, der Floyd-Warshall auf großen Graphen ausschließt.

10. Wo welcher Algorithmus eingesetzt wird

11. Immer noch ein aktives Forschungsgebiet

Kürzeste Wege gehören zu den ältesten Problemen der Algorithmik, und die Schranken bewegen sich noch immer. 2022 zeigten Aaron Bernstein, Danupon Nanongkai und Christian Wulff-Nilsen, dass kürzeste Wege von einer Quelle aus mit negativen ganzzahligen Gewichten in nahezu linearer Zeit lösbar sind, weit unter dem O(V E) von Bellman-Ford. 2024 bewiesen Bernhard Haeupler und Koautoren, dass der Algorithmus von Dijkstra mit einem geeignet entworfenen Heap universell optimal für das Ordnen von Knoten nach Distanz ist: Auf jedem festen Graphen liegt sein schlechtester Fall über alle Kantengewichte innerhalb eines konstanten Faktors des Besten, was irgendein Algorithmus leisten kann. Und 2025 stellten Ran Duan und Koautoren einen deterministischen Algorithmus für gerichtete Graphen mit nichtnegativen Gewichten vor, der in O(E log2/3 V) Zeit läuft und auf dünnen Graphen das O(E + V log V) von Dijkstra schlägt. Er überwindet, was lange als Sortierschranke galt, weil er, anders als Dijkstra, die Knoten nicht in der Reihenfolge ihrer Distanz ausgibt. Jede der drei Arbeiten gewann einen Best-Paper-Award. Keiner dieser Algorithmen hat die drei Klassiker bisher im Alltagscode ersetzt, wo Einfachheit und kleine Konstanten mehr zählen.

12. Häufige Fehler

FehlerWarum es falsch istAbhilfe
Dijkstra auf einem Graphen mit negativen Kanten laufen lassenAbgeschlossene Knoten werden nie erneut besucht; im Beispiel sind A, C und T falschBellman-Ford verwenden, oder Johnson für alle Paare
Eine Konstante addieren, um Gewichte nichtnegativ zu machenBestraft Routen mit mehr Kanten und ändert, welche Route die kürzeste istMit Potenzialen umgewichten, wie es der Johnson-Algorithmus tut
Negative Zyklen ignorierenDistanzen sind minus unendlich; jede gemeldete Zahl ist bedeutungslosEine zusätzliche Bellman-Ford-Runde laufen lassen oder die Floyd-Warshall-Diagonale prüfen
Eine negative Kante in einem ungerichteten GraphenSie hin und zurück zu begehen, ist ein negativer ZyklusDas Problem als kürzeste einfache Wege behandeln, was andere Methoden erfordert
Floyd-Warshall für eine einzige Quelle verwendenO(V3) Arbeit für eine Zeile der TabelleDijkstra oder Bellman-Ford verwenden
Floyd-Warshall auf einem riesigen dünnen Graphen verwendenDie V mal V große Tabelle passt eventuell nicht in den SpeicherDijkstra oder Johnson von den benötigten Quellen aus laufen lassen
Verwendung von INF + w mit einer festen großen GanzzahlUnendlich plus ein negatives Gewicht sieht wie eine echte Distanz aus oder läuft überRelaxationen von Knoten überspringen, die noch auf unendlich stehen
Bellman-Ford für genau V Runden laufen lassen und dem Ergebnis vertrauenRunde V ist die Testrunde; Änderungen dort bedeuten einen negativen ZyklusErst V - 1 Runden laufen lassen, dann noch einmal prüfen

13. Glossar

BegriffBedeutung
Kürzeste Wege von einer Quelle aus (Single-Source)Distanzen von einem Knoten zu jedem anderen Knoten
Kürzeste Wege zwischen allen PaarenDistanzen zwischen jedem geordneten Paar von Knoten
RelaxationPrüfen, ob eine Route über eine weitere Kante günstiger ist, und sie in diesem Fall festhalten
Abgeschlossener KnotenIm Algorithmus von Dijkstra ein Knoten, dessen Distanz für endgültig erklärt wurde
Negative KanteEine Kante mit einem Gewicht unter null
Negativer ZyklusEin Zyklus, dessen Gewichte sich zu weniger als null summieren
PrioritätswarteschlangeEine Struktur, die stets den kleinsten Schlüssel liefert; meist ein binärer Heap
PotenzialEine Zahl h(v) pro Knoten, mit der Kanten umgewichtet werden, ohne zu ändern, welche Routen die kürzesten sind
Dichter GraphEin Graph, in dem ein großer Anteil aller Knotenpaare durch Kanten verbunden ist
Dünner GraphEin Graph mit wenigen Kanten im Vergleich zu V2, typischerweise ein kleines Vielfaches von V
Transitive HülleDie Tabelle, welche Knoten welche erreichen können

14. Häufig gestellte Fragen

Was ist der Unterschied zwischen Dijkstra, Bellman-Ford und Floyd-Warshall?

Dijkstra und Bellman-Ford berechnen Distanzen von einem Quellknoten zu jedem anderen Knoten, während Floyd-Warshall die Distanz zwischen jedem Paar von Knoten berechnet. Dijkstra ist der schnellste, setzt aber nichtnegative Kantengewichte voraus. Bellman-Ford verarbeitet negative Gewichte und erkennt negative Zyklen in O(VE) Zeit. Floyd-Warshall verarbeitet ebenfalls negative Gewichte und erkennt negative Zyklen, in O(V hoch 3) Zeit und mit O(V hoch 2) Speicher.

Warum funktioniert Dijkstra nicht mit negativen Gewichten?

Dijkstra erklärt die Distanz eines Knotens für endgültig, sobald er der nächstgelegene offene Knoten ist, und besucht ihn nie wieder. Das ist nur sicher, wenn keine spätere Kante die Kosten senken kann. Eine negative Kante kann genau das, also kann Dijkstra einen Knoten zu früh abschließen und Distanzen melden, die zu hoch sind, ohne jede Warnung.

Ist Bellman-Ford langsamer als Dijkstra?

Ja, wann immer beide anwendbar sind. Bellman-Ford läuft in O(VE) Zeit, Dijkstra mit binärem Heap dagegen in O((V + E) log V). Auf einem Graphen mit 10.000 Knoten und 50.000 Kanten sind das bis zu 500 Millionen Kantenrelaxationen gegenüber etwa einer Million Heap-Schritten. Verwenden Sie Bellman-Ford nur, wenn Gewichte negativ sein können oder ein negativer Zyklus erkannt werden muss.

Wann sollten Sie Floyd-Warshall verwenden, statt Dijkstra von jedem Knoten aus zu starten?

Verwenden Sie Floyd-Warshall, wenn Sie die Distanzen aller Paare in einem kleinen Graphen mit bis zu einigen hundert Knoten brauchen oder in einem dichten Graphen, in dem die meisten Knotenpaare durch Kanten verbunden sind, und auch dann, wenn Gewichte negativ sein können. Auf großen dünnen Graphen ist Dijkstra von jedem Knoten aus, oder der Johnson-Algorithmus, wenn Gewichte negativ sein können, viel schneller und braucht weit weniger Speicher.

Wie erkennt man einen negativen Zyklus?

Lassen Sie Bellman-Ford V - 1 Runden laufen und dann eine weitere: Senkt die zusätzliche Runde noch eine Distanz, existiert ein negativer Zyklus, und wer den Vorgängerzeigern vom geänderten Knoten aus folgt, gelangt auf ihn. Bei Floyd-Warshall existiert ein negativer Zyklus genau dann, wenn ein Eintrag auf der Diagonale der finalen Distanzmatrix negativ ist. Dijkstra kann negative Zyklen nicht erkennen.

Was ist der Johnson-Algorithmus?

Der Johnson-Algorithmus berechnet kürzeste Wege zwischen allen Paaren in dünnen Graphen mit negativen Kanten. Er lässt Bellman-Ford einmal laufen, um für jeden Knoten ein Potenzial h(v) zu erhalten, gewichtet jede Kante zu w(u, v) + h(u) - h(v) um, was nie negativ ist und dieselben kürzesten Wege erhält, und lässt dann Dijkstra von jedem Knoten aus laufen. Seine Laufzeit beträgt mit binärem Heap O(VE + V(V + E) log V).

15. Quellen

Die Algorithmen, Schranken und Zuschreibungen oben stammen aus diesen Quellen, in chronologischer Reihenfolge.

  1. Shimbel, A. (1955). "Structure in Communication Nets." In Proceedings of the Symposium on Information Networks, 199 bis 203. Brooklyn: Polytechnic Press of the Polytechnic Institute of Brooklyn.
  2. Ford, L. R. (1956). Network Flow Theory. Paper P-923. Santa Monica: RAND Corporation.
  3. Bellman, R. (1958). "On a Routing Problem." Quarterly of Applied Mathematics 16(1), 87 bis 90.
  4. Dijkstra, E. W. (1959). "A Note on Two Problems in Connexion with Graphs." Numerische Mathematik 1, 269 bis 271.
  5. Moore, E. F. (1959). "The Shortest Path Through a Maze." In Proceedings of an International Symposium on the Theory of Switching, 285 bis 292. Cambridge, MA: Harvard University Press.
  6. Roy, B. (1959). "Transitivité et connexité." Comptes Rendus de l'Académie des Sciences 249, 216 bis 218.
  7. Floyd, R. W. (1962). "Algorithm 97: Shortest Path." Communications of the ACM 5(6), 345.
  8. Warshall, S. (1962). "A Theorem on Boolean Matrices." Journal of the ACM 9(1), 11 bis 12.
  9. Johnson, D. B. (1973). "A Note on Dijkstra's Shortest Path Algorithm." Journal of the ACM 20(3), 385 bis 388.
  10. Johnson, D. B. (1977). "Efficient Algorithms for Shortest Paths in Sparse Networks." Journal of the ACM 24(1), 1 bis 13.
  11. Fredman, M. L. und Tarjan, R. E. (1987). "Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms." Journal of the ACM 34(3), 596 bis 615.
  12. Moy, J. (1998). "OSPF Version 2." RFC 2328. Internet Engineering Task Force.
  13. Malkin, G. (1998). "RIP Version 2." RFC 2453. Internet Engineering Task Force.
  14. Cormen, T. H., Leiserson, C. E., Rivest, R. L. und Stein, C. (2009). Introduction to Algorithms, 3. Auflage. Cambridge, MA: MIT Press.
  15. Frana, P. L. und Misa, T. J. (2010). "An Interview with Edsger W. Dijkstra." Communications of the ACM 53(8), 41 bis 47.
  16. Hougardy, S. (2010). "The Floyd-Warshall Algorithm on Graphs with Negative Cycles." Information Processing Letters 110(8 bis 9), 279 bis 281.
  17. Sedgewick, R. und Wayne, K. (2011). Algorithms, 4. Auflage. Upper Saddle River, NJ: Addison-Wesley.
  18. Schrijver, A. (2012). "On the History of the Shortest Path Problem." Documenta Mathematica, Extra Volume ISMP, 155 bis 167.
  19. Bernstein, A., Nanongkai, D. und Wulff-Nilsen, C. (2022). "Negative-Weight Single-Source Shortest Paths in Near-linear Time." Proceedings of the 63rd IEEE Symposium on Foundations of Computer Science, 600 bis 611.
  20. Haeupler, B., Hladík, R., Rozhoň, V., Tarjan, R. E. und Tětek, J. (2024). "Universal Optimality of Dijkstra via Beyond-Worst-Case Heaps." Proceedings of the 65th IEEE Symposium on Foundations of Computer Science, 2099 bis 2130.
  21. Duan, R., Mao, J., Mao, X., Shu, X. und Yin, L. (2025). "Breaking the Sorting Barrier for Directed Single-Source Shortest Paths." Proceedings of the 57th ACM Symposium on Theory of Computing, 36 bis 44.

Sehen Sie der negativen Kante bei der Arbeit zu

Bauen Sie das Beispiel im Visualisierer nach, setzen Sie die Kante von B nach A auf -4 und starten Sie Bellman-Ford von S aus. Beobachten Sie, wie die Rückerstattung A erreicht und dann an C und T weitergegeben wird.

Visualisierer öffnen

Lassen Sie die drei Algorithmen gegeneinander antreten

Führen Sie Dijkstra Schritt für Schritt auf Ihrem eigenen Graphen aus und beobachten Sie, welchen Knoten er als Nächstes abschließt. Machen Sie dann ein Gewicht negativ und vergleichen Sie mit Bellman-Ford.

Dijkstra-Visualisierer starten