Skip to content

Commit d8bc237

Browse files
Add Floyd-Warshall algorithm with documentation, comments and test cases
1 parent 5e14106 commit d8bc237

1 file changed

Lines changed: 97 additions & 0 deletions

File tree

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)