Bellman-Ford-Algorithmus
Zuletzt aktualisiert
Bellman-Ford findet den kürzesten Weg von einem Quellknoten zu jedem anderen Knoten und funktioniert im Gegensatz zu Dijkstra auch, wenn einige Kantengewichte negativ sind. Er arbeitet durch Brute-Force-Relaxierung: Er durchläuft wiederholt jede Kante und aktualisiert die Distanz des Zielknotens, falls der Weg über diese Kante eine kürzere Distanz ergibt. Drücke oben auf Wiedergabe, um zu sehen, wie die vorläufigen Distanzen Durchlauf für Durchlauf sinken.
Nach V - 1 Durchläufen über alle Kanten ist jeder kürzeste Weg (der höchstens V - 1 Kanten haben kann) gefunden. Das macht ihn O(V · E) - langsamer als Dijkstra, aber er verarbeitet negative Gewichte und kann einen negativen Zyklus erkennen (relaxiert ein V-ter Durchlauf noch eine Kante, existiert kein kürzester Weg).
Zeit- und Speicherkomplexität
| Maß | Komplexität | Anmerkungen |
|---|---|---|
| Zeit | O(V · E) | V − 1 Durchläufe über alle E Kanten |
| Speicher | O(V) | Eine Distanz pro Knoten |
| Negative Gewichte | Unterstützt | Sein entscheidender Vorteil gegenüber Dijkstra |
| Negative Zyklen | Erkennbar | Ein relaxierender V-ter Durchlauf signalisiert einen |
Schritt für Schritt
| Schritt | Was passiert |
|---|---|
| 1 | Setze die Quelldistanz auf 0 und alle anderen auf unendlich. |
| 2 | Wiederhole den nächsten Schritt V − 1 Mal. |
| 3 | Prüfe für jede Kante (u → v, w), ob dist[u] + w < dist[v]. |
| 4 | Falls ja, relaxiere sie: setze dist[v] = dist[u] + w. |
| 5 | Höre früher auf, wenn ein voller Durchlauf nichts ändert. |
Durchgerechnetes Beispiel
Quelle S auf 4 Knoten S, A, B, C mit den Kanten S→A (4), S→B (5), A→C (3), B→A (-3), in dieser Reihenfolge relaxiert. Die Distanzen beginnen bei S=0 und alles andere bei ∞:
| Durchlauf | Distanzen [S, A, B, C] | Aktion |
|---|---|---|
| Init | [0, ∞, ∞, ∞] | Die Quelldistanz ist 0, alle anderen unendlich. |
| 1 | [0, 2, 5, 7] | Relaxiert S→A auf 4, S→B auf 5, A→C auf 7, dann senkt B→A A auf 5 + (-3) = 2. |
| 2 | [0, 2, 5, 5] | A→C verbessert C nun auf 2 + 3 = 5; keine andere Kante relaxiert. |
| 3 | [0, 2, 5, 5] | Keine Kante relaxiert, also sind die Distanzen endgültig: A kostet 2 über S→B→A. |
Wann Bellman-Ford verwenden
| Verwende ihn, wenn | Vermeide ihn, wenn |
|---|---|
| Kantengewichte negativ sein können. | Alle Gewichte nicht-negativ sind (Dijkstra ist schneller). |
| Du negative Zyklen erkennen musst. | Der Graph riesig und dicht ist - O(V · E) ist zu langsam. |
| Der Graph klein oder dünn besetzt ist. | Du nur eine einzelne Quelle-Ziel-Abfrage auf einem großen Graphen brauchst. |
| Du eine einfache, leicht umsetzbare Basis brauchst. | Die Gewichte nicht-negativ sind und Latenz zählt. |
Bellman-Ford Algorithm-Code
Eine saubere, lauffähige Bellman-Ford Algorithm-Implementierung in Python, JavaScript, Java, C++, C. Wähle eine Sprache, kopiere den Code oder öffne ihn vorgeladen im Coddy-Playground.
Bellman-Ford Algorithm-Code in Python
1def bellman_ford(vertices, edges, start):2 dist = {v: float("inf") for v in vertices}3 dist[start] = 04 # Relax every edge V-1 times5 for _ in range(len(vertices) - 1):6 for u, v, w in edges:7 if dist[u] + w < dist[v]:8 dist[v] = dist[u] + w9 # One more pass: any improvement means a negative cycle10 for u, v, w in edges:11 if dist[u] + w < dist[v]:12 raise ValueError("Graph contains a negative-weight cycle")13 return dist14
15
16vertices = ["A", "B", "C", "D", "E"]17edges = [18 ("A", "B", 6), ("A", "C", 7), ("B", "D", 5), ("B", "E", -4),19 ("C", "D", -3), ("D", "B", -2), ("E", "D", 7),20]21
22for node, d in bellman_ford(vertices, edges, "A").items():23 print(f"A -> {node}: {d}")Bellman-Ford Algorithm-Code in JavaScript
1const nodes = ["A", "B", "C", "D", "E"];2const edges = [3 ["A", "B", 6], ["A", "C", 7], ["B", "D", 5], ["B", "E", -4],4 ["C", "D", -3], ["D", "B", -2], ["E", "A", 2],5];6
7function bellmanFord(source) {8 const dist = Object.fromEntries(nodes.map((n) => [n, Infinity]));9 dist[source] = 0;10 // Relax every edge V-1 times11 for (let i = 0; i < nodes.length - 1; i++) {12 for (const [u, v, w] of edges) {13 if (dist[u] + w < dist[v]) dist[v] = dist[u] + w;14 }15 }16 // One more pass: any improvement means a negative cycle17 for (const [u, v, w] of edges) {18 if (dist[u] + w < dist[v]) throw new Error("Negative cycle detected");19 }20 return dist;21}22
23console.log("Shortest distances from A:", bellmanFord("A"));Bellman-Ford Algorithm-Code in Java
1import java.util.Arrays;2
3public class Main {4 public static void main(String[] args) {5 int n = 5;6 // Directed edges {from, to, weight}; negative weights allowed7 int[][] edges = {8 {0, 1, 6}, {0, 2, 7}, {1, 3, 5}, {1, 2, 8},9 {2, 4, 9}, {3, 1, -2}, {4, 3, 7}, {2, 3, -3}10 };11 int[] dist = new int[n];12 Arrays.fill(dist, Integer.MAX_VALUE);13 dist[0] = 0;14
15 // Relax every edge V-1 times16 for (int i = 0; i < n - 1; i++) {17 for (int[] e : edges) {18 if (dist[e[0]] != Integer.MAX_VALUE && dist[e[0]] + e[2] < dist[e[1]]) {19 dist[e[1]] = dist[e[0]] + e[2];20 }21 }22 }23
24 // One more pass: any improvement means a negative cycle25 boolean hasNegativeCycle = false;26 for (int[] e : edges) {27 if (dist[e[0]] != Integer.MAX_VALUE && dist[e[0]] + e[2] < dist[e[1]]) {28 hasNegativeCycle = true;29 }30 }31
32 System.out.println("Distances from 0: " + Arrays.toString(dist));33 System.out.println("Negative cycle: " + hasNegativeCycle);34 }35}Bellman-Ford Algorithm-Code in C++
1#include <iostream>2#include <vector>3
4struct Edge {5 int from, to, weight;6};7
8int main() {9 const int INF = 1000000000;10 const int n = 5;11 std::vector<Edge> edges = {12 {0, 1, 6}, {0, 2, 7}, {1, 3, 5}, {2, 3, -3},13 {1, 4, -4}, {3, 4, 9}, {2, 1, -2},14 };15 std::vector<int> dist(n, INF);16 dist[0] = 0;17 // Relax every edge V-1 times18 for (int pass = 0; pass < n - 1; ++pass) {19 for (const Edge& e : edges) {20 if (dist[e.from] != INF && dist[e.from] + e.weight < dist[e.to]) {21 dist[e.to] = dist[e.from] + e.weight;22 }23 }24 }25 // One extra pass: any improvement means a negative cycle26 bool negativeCycle = false;27 for (const Edge& e : edges) {28 if (dist[e.from] != INF && dist[e.from] + e.weight < dist[e.to]) {29 negativeCycle = true;30 }31 }32 if (negativeCycle) {33 std::cout << "Negative cycle detected\n";34 } else {35 for (int v = 0; v < n; ++v) {36 std::cout << "Distance 0 -> " << v << ": " << dist[v] << "\n";37 }38 }39 return 0;40}Bellman-Ford Algorithm-Code in C
1#include <stdbool.h>2#include <stdio.h>3
4#define N 55#define INF 10000000006
7typedef struct {8 int from, to, weight;9} Edge;10
11int main(void) {12 Edge edges[] = {13 {0, 1, 6}, {0, 2, 7}, {1, 3, 5}, {2, 3, -3},14 {1, 4, -4}, {3, 4, 9}, {2, 1, -2},15 };16 int m = sizeof(edges) / sizeof(edges[0]);17 int dist[N];18 for (int v = 0; v < N; v++) dist[v] = INF;19 dist[0] = 0;20 // Relax every edge V-1 times21 for (int pass = 0; pass < N - 1; pass++) {22 for (int i = 0; i < m; i++) {23 Edge e = edges[i];24 if (dist[e.from] != INF && dist[e.from] + e.weight < dist[e.to]) {25 dist[e.to] = dist[e.from] + e.weight;26 }27 }28 }29 // One extra pass: any improvement means a negative cycle30 bool negativeCycle = false;31 for (int i = 0; i < m; i++) {32 Edge e = edges[i];33 if (dist[e.from] != INF && dist[e.from] + e.weight < dist[e.to]) {34 negativeCycle = true;35 }36 }37 if (negativeCycle) {38 printf("Negative cycle detected\n");39 } else {40 for (int v = 0; v < N; v++) {41 printf("Distance 0 -> %d: %d\n", v, dist[v]);42 }43 }44 return 0;45}Bellman-Ford FAQ
Wie ist die Zeitkomplexität von Bellman-Ford?
O(V · E) Zeit: Er macht V - 1 Durchläufe, und jeder Durchlauf relaxiert alle E Kanten. Das ist langsamer als Dijkstras O((V + E) log V), was der Preis für die Unterstützung negativer Kantengewichte ist.Wann sollte ich Bellman-Ford statt Dijkstra verwenden?
Wie erkennt Bellman-Ford einen negativen Zyklus?
V - 1 Relaxierungsdurchläufen sind alle kürzesten Wege final, sofern kein negativer Zyklus existiert. Kann ein weiterer Durchlauf noch eine Kante relaxieren, dann ist ein negativer Zyklus erreichbar, und die kürzesten Wege sind undefiniert (man könnte endlos schleifen, um die Kosten zu senken).Was ist der Unterschied zwischen Bellman-Ford und Floyd-Warshall?
O(V · E), während Floyd-Warshall die kürzesten Wege zwischen allen Paaren in O(V³) berechnet. Auf einem dichten Graphen kostet es O(V² · E), Bellman-Ford von jeder Quelle aus laufen zu lassen, daher ist Floyd-Warshall meist die bessere Wahl, wenn du alle Paare brauchst. Beide verarbeiten negative Kanten und können negative Zyklen anzeigen.Warum benötigt Bellman-Ford genau V - 1 Durchläufe?
V - 1 Kanten, denn ein Weg, der mehr als V Knoten besucht, muss einen wiederholen. Jeder Durchlauf garantiert, dass mindestens eine weitere Kante jedes kürzesten Wegs korrekt relaxiert wird, also reichen V - 1 Durchläufe, um sie alle festzulegen. Ein häufiges Missverständnis ist, dass mehr Durchläufe das Ergebnis verbessern - das tun sie nie, es sei denn, es existiert ein negativer Zyklus.Kann Bellman-Ford früher stoppen?
V - 1 Durchläufen aufhören. Diese Frühabbruch-Optimierung macht ihn auf schnell konvergierenden Graphen oft deutlich schneller, auch wenn die Worst-Case-Schranke O(V · E) bleibt.