Lably
Skip to content

Bellman–Ford algorithm

Algorithm for finding the shortest paths in graphs

  • 10 min read
  • 5 chapters

Related on Lably

Through Algorithm, Directed graph and Big O notation

Negative edge weights are found in various applications of graphs. This is why this algorithm is useful. If a graph contains a "negative cycle" (i.e. a cycle whose edges sum to a negative value) that is reachable from the source, then there is no cheapest path: any path that has a point on the negative cycle can be made cheaper by one more walk around the negative cycle. In such a case, the Bellman–Ford algorithm can detect and report the negative cycle.

Algorithm

Like Dijkstra's algorithm, Bellman–Ford proceeds by relaxation, in which approximations to the correct distance are replaced by better ones until they eventually reach the solution. In both algorithms, the approximate distance to each vertex is always an overestimate of the true distance, and is replaced by the minimum of its old value and the length of a newly found path.

However, Dijkstra's algorithm uses a priority queue to greedily select the closest vertex that has not yet been processed, and performs this relaxation process on all of its outgoing edges; by contrast, the Bellman–Ford algorithm simply relaxes all the edges, and does this |V|−1 times, where |V| is the number of vertices in the graph.

In each of these repetitions, the number of vertices with correctly calculated distances grows, from which it follows that eventually all vertices will have their correct distances. This method allows the Bellman–Ford algorithm to be applied to a wider class of inputs than Dijkstra's algorithm. The intermediate answers and the choices among equally short paths depend on the order of edges relaxed, but the final distances remain the same.

Bellman–Ford runs in O(|V|⋅|E|) time, where |V| and |E| are the number of vertices and edges respectively.

<b id="mwWg">function</b> BellmanFord(<i id="mwWw">list</i> vertices, <i id="mwXA">list</i> edges, <i id="mwXQ">vertex</i> source) <b id="mwXg">is</b> <i id="mwXw">// This implementation takes in a graph, represented as</i> <i id="mwYA">// lists of vertices (represented as integers [0..n-1]) and</i> <i id="mwYQ">// edges, and fills two arrays (distance and predecessor)</i> <i id="mwYg">// holding the shortest path from the source to each vertex</i> distance := <i id="mwYw">list</i> of size <i id="mwZA">n</i> predecessor := <i id="mwZQ">list</i> of size <i id="mwZg">n</i> <i id="mwZw">// Step 1: initialize graph</i> <b id="mwaA">for each</b> vertex v <b id="mwaQ">in</b> vertices <b id="mwag">do</b> // Initialize the distance to all vertices to infinity distance[v] := <b id="mwaw">inf</b> // And having a null predecessor predecessor[v] := <b id="mwbA">null</b> // The distance from the source to itself is zero distance[source] := 0 <i id="mwbQ">// Step 2: relax edges repeatedly</i> <b id="mwbg">repeat</b> |V|−1 <b id="mwbw">times</b>: <b id="mwcA">for each</b> edge (u, v) <b id="mwcQ">with</b> weight w <b id="mwcg">in</b> edges <b id="mwcw">do</b> <b id="mwdA">if</b> distance[u] + w < distance[v] <b id="mwdQ">then</b> distance[v] := distance[u] + w predecessor[v] := u <i id="mwdg">// Step 3: check for negative-weight cycles</i> <b id="mwdw">for each</b> edge (u, v) <b id="mweA">with</b> weight w <b id="mweQ">in</b> edges <b id="mweg">do</b> <b id="mwew">if</b> distance[u] + w < distance[v] <b id="mwfA">then</b> predecessor[v] := u <i id="mwfQ">// A negative cycle exists;</i> <i id="mwfg">// find a vertex on the cycle</i> visited := <i id="mwfw">list</i> of size <i id="mwgA">n</i> initialized with <b id="mwgQ">false</b> visited[v] := <b id="mwgg">true</b> <b id="mwgw">while</b> <i id="mwhA">not</i> visited[u] <b id="mwhQ">do</b> visited[u] := <b id="mwhg">true</b> u := predecessor[u] <i id="mwhw">// u is a vertex in a negative cycle,</i> <i id="mwiA">// find the cycle itself</i> ncycle := [u] v := predecessor[u] <b id="mwiQ">while</b> v != u <b id="mwig">do</b> ncycle := concatenate([v], ncycle) v := predecessor[v] <b id="mwiw">error</b> "Graph contains a negative-weight cycle", ncycle <b id="mwjA">return</b> distance, predecessor

Simply put, the algorithm initializes the distance to the source to 0 and all other nodes to infinity. Then for all edges, if the distance to the destination can be shortened by taking the edge, the distance is updated to the new lower value.

The core of the algorithm is a loop that scans across all edges at every loop. For every i≤|V|−1, at the end of the i-th iteration, from any vertex v, following the predecessor trail recorded in predecessor yields a path that has a total weight that is at most distance[v], and further, distance[v] is a lower bound to the length of any path from source to v that uses at most i edges.

Since the longest possible path without a cycle can be |V|−1 edges, the edges must be scanned |V|−1 times to ensure the shortest path has been found for all nodes. A final scan of all the edges is performed and if any distance is updated, then a path of length |V| edges has been found which can only occur if at least one negative cycle exists in the graph.

The edge (u, v) that is found in step 3 must be reachable from a negative cycle, but it isn't necessarily part of the cycle itself, which is why it's necessary to follow the path of predecessors backwards until a cycle is detected. The above pseudo-code uses a Boolean array (visited) to find a vertex on the cycle, but any cycle finding algorithm can be used to find a vertex on the cycle.

A common improvement when implementing the algorithm is to return early when an iteration of step 2 fails to relax any edges, which implies all shortest paths have been found, and therefore there are no negative cycles. In that case, the complexity of the algorithm is reduced from O(|V|⋅|E|) to O(l⋅|E|) where l is the maximum length of a shortest path in the graph.

Sign in to keep reading

4 more chapters are free with a Lably account.

More in Discrete mathematics