Bellman Ford Algorithm Negative Weights in DSA Rust - Time & Space Complexity
We want to understand how the time needed to find shortest paths changes as the graph grows.
How does the Bellman Ford algorithm behave when there are negative weights?
Analyze the time complexity of the following code snippet.
fn bellman_ford(edges: &[(usize, usize, i32)], vertices: usize, source: usize) -> Vec {
let mut dist = vec![i32::MAX; vertices];
dist[source] = 0;
for _ in 0..vertices - 1 {
for &(u, v, w) in edges {
if dist[u] != i32::MAX && dist[u] + w < dist[v] {
dist[v] = dist[u] + w;
}
}
}
dist
}
This code finds shortest paths from a source to all vertices, even with negative weights, by relaxing edges multiple times.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The inner loop that goes through all edges to update distances.
- How many times: The outer loop runs (vertices - 1) times, repeating the inner loop each time.
Each vertex causes the algorithm to check all edges again, so work grows with both vertices and edges.
| Input Size (vertices, edges) | Approx. Operations |
|---|---|
| 10 vertices, 20 edges | ~ 9 * 20 = 180 |
| 100 vertices, 500 edges | ~ 99 * 500 = 49,500 |
| 1000 vertices, 2000 edges | ~ 999 * 2000 = 1,998,000 |
Pattern observation: Operations grow roughly by multiplying vertices and edges, so doubling either doubles the work.
Time Complexity: O(V * E)
This means the time needed grows by the number of vertices times the number of edges in the graph.
[X] Wrong: "The algorithm only needs to check edges once, so it runs in O(E) time."
[OK] Correct: The algorithm must repeat edge checks (V-1) times to ensure shortest paths, especially with negative weights.
Understanding this time complexity helps you explain how algorithms handle tricky cases like negative weights and why some algorithms take longer.
"What if we stopped the algorithm early when no distances change in an iteration? How would that affect the time complexity?"
Practice
Bellman-Ford algorithm compared to Dijkstra's algorithm?Solution
Step 1: Understand the limitation of Dijkstra's algorithm
Dijkstra's algorithm cannot correctly handle graphs with negative weight edges because it assumes that once a node's shortest path is found, it cannot be improved.Step 2: Identify Bellman-Ford's capability
Bellman-Ford algorithm can handle negative weight edges and detect negative weight cycles, making it more versatile for such graphs.Final Answer:
It can handle graphs with negative weight edges. -> Option AQuick Check:
Negative weights handled = A [OK]
- Thinking Bellman-Ford is always faster
- Assuming it uses less memory
- Believing it only works on acyclic graphs
Solution
Step 1: Understand initialization for Bellman-Ford
Distances to all vertices except the source should be set to a very large number (infinity), here represented byi32::MAX. The source vertex distance is set to 0.Step 2: Check each option
let mut dist = vec![i32::MAX; 5]; dist[0] = 0; correctly sets all distances toi32::MAXand source (index 0) to 0. Others either invert this or use wrong values.Final Answer:
let mut dist = vec![i32::MAX; 5]; dist[0] = 0; -> Option DQuick Check:
Initialize distances with max except source 0 = D [OK]
- Setting source distance to max instead of zero
- Initializing all distances to zero
- Using i32::MIN which is negative infinity
edges = [(0, 1, 4), (0, 2, 5), (1, 2, -3), (2, 3, 4)] starting from vertex 0? Initial distances:
[0, i32::MAX, i32::MAX, i32::MAX]Solution
Step 1: Relax edges once from initial distances
Start distances: [0, MAX, MAX, MAX]. Relax edges:
- Edge (0->1,4): dist[1] = min(MAX, 0+4) = 4
- Edge (0->2,5): dist[2] = min(MAX, 0+5) = 5
- Edge (1->2,-3): dist[2] = min(5, 4-3) = 1
- Edge (2->3,4): dist[3] = min(MAX, 1+4) = 5Step 2: Determine distances after one iteration
After relaxing all edges once, distances are [0, 4, 1, 5]. But since the question asks after one iteration (one full pass), all edges are relaxed once, so the final distances are [0, 4, 1, 5].Final Answer:
[0, 4, 1, 5] -> Option BQuick Check:
Relax edges once = [0,4,1,5] [OK]
- Not relaxing edges in order
- Ignoring negative edge effect
- Stopping after partial relaxation
for _ in 0..graph.len() {
for &(u, v, w) in &edges {
if dist[u] != i32::MAX && dist[u] + w < dist[v] {
dist[v] = dist[u] + w;
}
}
}
// Check for negative cycle
for &(u, v, w) in &edges {
if dist[u] != i32::MAX && dist[u] + w < dist[v] {
return true; // Negative cycle detected
}
}
return false;Solution
Step 1: Understand Bellman-Ford iteration count
Bellman-Ford requires relaxing edges exactly |V| - 1 times (where |V| is number of vertices) to find shortest paths. Running it |V| times can cause incorrect behavior.Step 2: Identify the bug in loop count
The code runs the outer loop graph.len() times (equal to |V|), which is one too many. This can cause false detection of negative cycles.Final Answer:
The outer loop should run graph.len() - 1 times, not graph.len() times. -> Option AQuick Check:
Relax edges |V|-1 times = A [OK]
- Running relaxation too many times
- Checking negative cycle inside main loop
- Incorrect distance initialization
[(0, 1, 1), (1, 2, -1), (2, 3, -1), (3, 1, -1)]. Using Bellman-Ford starting from vertex 0, what will the algorithm detect?Solution
Step 1: Analyze the cycle formed by edges
The edges (1->2, -1), (2->3, -1), and (3->1, -1) form a cycle with total weight -3, which is negative.Step 2: Bellman-Ford detects negative cycles reachable from source
Since this negative cycle is reachable from vertex 0 via edge (0->1), Bellman-Ford will detect it during the negative cycle check.Final Answer:
Negative cycle detected involving vertices 1, 2, and 3. -> Option CQuick Check:
Negative cycle in reachable cycle = C [OK]
- Ignoring negative cycle presence
- Assuming algorithm runs infinitely
- Thinking only positive cycles exist
Ready to prove it?
Pass the test and this pattern moves unknown
