Skip to content

Commit c38cdd9

Browse files
Merge pull request #549 from komalsrivastava282/floyd-warshall-python
Added Floyd-Warshall Algorithm in Python
2 parents 92a8d2c + d8bc237 commit c38cdd9

2 files changed

Lines changed: 197 additions & 0 deletions

File tree

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
"""
2+
Bellman-Ford Algorithm
3+
----------------------
4+
Description:
5+
Computes shortest paths from a single source vertex to all other vertices in a weighted graph.
6+
Supports negative weight edges and detects negative weight cycles.
7+
8+
Usage:
9+
Call bellman_ford(vertices, edges, source) with:
10+
- vertices: list of node labels
11+
- edges: list of tuples (u, v, weight)
12+
- source: starting node label
13+
14+
Time Complexity: O(V * E)
15+
Space Complexity: O(V)
16+
17+
18+
"""
19+
20+
def bellman_ford(vertices, edges, source):
21+
# Validate that the source exists in the graph
22+
if source not in vertices:
23+
raise ValueError(f"Source vertex '{source}' not found in graph.")
24+
25+
# Validate that all edge endpoints exist in the graph
26+
for u, v, _ in edges:
27+
if u not in vertices or v not in vertices:
28+
raise ValueError(f"Edge contains undefined vertex: ({u}, {v})")
29+
30+
# Step 1: Initialize distances from source to all vertices as infinite
31+
distance = {v: float('inf') for v in vertices}
32+
distance[source] = 0
33+
34+
# Step 2: Relax all edges |V| - 1 times
35+
for _ in range(len(vertices) - 1):
36+
for u, v, w in edges:
37+
# If the current path offers a shorter route, update the distance
38+
if distance[u] != float('inf') and distance[u] + w < distance[v]:
39+
distance[v] = distance[u] + w
40+
41+
# Step 3: Check for negative weight cycles
42+
for u, v, w in edges:
43+
if distance[u] != float('inf') and distance[u] + w < distance[v]:
44+
raise ValueError("Graph contains a negative weight cycle.")
45+
46+
return distance
47+
48+
49+
# ------------------ Test Cases ------------------
50+
51+
def run_tests():
52+
print("Running Bellman-Ford test cases...\n")
53+
54+
# Test Case 1: Basic graph with positive weights
55+
vertices1 = ['A', 'B', 'C', 'D']
56+
edges1 = [
57+
('A', 'B', 1),
58+
('B', 'C', 3),
59+
('A', 'C', 10),
60+
('C', 'D', 2),
61+
('B', 'D', 2)
62+
]
63+
print("Test Case 1:", bellman_ford(vertices1, edges1, 'A'))
64+
65+
# Test Case 2: Graph with negative weights but no cycle
66+
vertices2 = ['X', 'Y', 'Z']
67+
edges2 = [
68+
('X', 'Y', 4),
69+
('Y', 'Z', -2),
70+
('X', 'Z', 5)
71+
]
72+
print("Test Case 2:", bellman_ford(vertices2, edges2, 'X'))
73+
74+
# Test Case 3: Graph with a negative weight cycle
75+
vertices3 = ['P', 'Q', 'R']
76+
edges3 = [
77+
('P', 'Q', 1),
78+
('Q', 'R', -1),
79+
('R', 'P', -1)
80+
]
81+
try:
82+
print("Test Case 3:", bellman_ford(vertices3, edges3, 'P'))
83+
except ValueError as e:
84+
print("Test Case 3: Exception caught -", e)
85+
86+
# Test Case 4: Invalid source vertex
87+
try:
88+
print("Test Case 4:", bellman_ford(['A', 'B'], [('A', 'B', 2)], 'Z'))
89+
except ValueError as e:
90+
print("Test Case 4: Exception caught -", e)
91+
92+
# Test Case 5: Edge with undefined vertex
93+
try:
94+
print("Test Case 5:", bellman_ford(['A', 'B'], [('A', 'C', 2)], 'A'))
95+
except ValueError as e:
96+
print("Test Case 5: Exception caught -", e)
97+
98+
99+
if __name__ == "__main__":
100+
run_tests()
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
"""
2+
Floyd-Warshall Algorithm
3+
------------------------
4+
Description:
5+
Computes shortest paths between all pairs of vertices in a weighted graph using dynamic programming.
6+
Supports negative edge weights but does not detect negative weight cycles.
7+
8+
Algorithm:
9+
- Initialize a distance matrix with infinity for all pairs except self-loops (0).
10+
- Populate the matrix with direct edge weights.
11+
- For each intermediate vertex k, update dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]).
12+
13+
Time Complexity: O(V^3)
14+
Space Complexity: O(V^2)
15+
16+
"""
17+
18+
def floyd_warshall(vertices, edges):
19+
# Validate that vertex list is not empty
20+
if not vertices:
21+
raise ValueError("Vertex list is empty.")
22+
23+
# Validate that all edge endpoints exist in the graph
24+
for u, v, _ in edges:
25+
if u not in vertices or v not in vertices:
26+
raise ValueError(f"Edge contains undefined vertex: ({u}, {v})")
27+
28+
# Step 1: Initialize distance matrix with infinity
29+
dist = {i: {j: float('inf') for j in vertices} for i in vertices}
30+
for v in vertices:
31+
dist[v][v] = 0 # Distance to self is zero
32+
33+
# Step 2: Populate initial edge weights
34+
for u, v, w in edges:
35+
dist[u][v] = w
36+
37+
# Step 3: Floyd-Warshall core logic
38+
for k in vertices:
39+
for i in vertices:
40+
for j in vertices:
41+
# If going through k offers a shorter path, update it
42+
if dist[i][k] + dist[k][j] < dist[i][j]:
43+
dist[i][j] = dist[i][k] + dist[k][j]
44+
45+
return dist
46+
47+
48+
# ------------------ Test Cases ------------------
49+
50+
def run_tests():
51+
print("Running Floyd-Warshall test cases...\n")
52+
53+
# Test Case 1: Basic graph
54+
vertices1 = ['A', 'B', 'C']
55+
edges1 = [
56+
('A', 'B', 4),
57+
('B', 'C', 1),
58+
('A', 'C', 10)
59+
]
60+
print("Test Case 1:")
61+
print(floyd_warshall(vertices1, edges1), "\n")
62+
63+
# Test Case 2: Graph with negative weights
64+
vertices2 = ['X', 'Y', 'Z']
65+
edges2 = [
66+
('X', 'Y', 3),
67+
('Y', 'Z', -2),
68+
('Z', 'X', 5)
69+
]
70+
print("Test Case 2:")
71+
print(floyd_warshall(vertices2, edges2), "\n")
72+
73+
# Test Case 3: Disconnected graph
74+
vertices3 = ['P', 'Q', 'R']
75+
edges3 = [
76+
('P', 'Q', 7)
77+
]
78+
print("Test Case 3:")
79+
print(floyd_warshall(vertices3, edges3), "\n")
80+
81+
# Test Case 4: Invalid edge vertex
82+
try:
83+
print("Test Case 4:")
84+
floyd_warshall(['A', 'B'], [('A', 'C', 2)])
85+
except ValueError as e:
86+
print("Exception caught -", e, "\n")
87+
88+
# Test Case 5: Empty vertex list
89+
try:
90+
print("Test Case 5:")
91+
floyd_warshall([], [('A', 'B', 1)])
92+
except ValueError as e:
93+
print("Exception caught -", e, "\n")
94+
95+
96+
if __name__ == "__main__":
97+
run_tests()

0 commit comments

Comments
 (0)