forked from OmkarPathak/pygorithm
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbellman_ford.py
More file actions
213 lines (165 loc) · 5.87 KB
/
bellman_ford.py
File metadata and controls
213 lines (165 loc) · 5.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
"""
Author: ADWAITA JADHAV
Created On: 4th October 2025
Bellman-Ford Algorithm for Single Source Shortest Path
Time Complexity: O(V * E) where V is vertices and E is edges
Space Complexity: O(V)
The Bellman-Ford algorithm finds shortest paths from a single source vertex
to all other vertices in a weighted graph. Unlike Dijkstra's algorithm,
it can handle negative edge weights and detect negative cycles.
"""
import inspect
def bellman_ford(graph, source):
"""
Find shortest paths from source to all vertices using Bellman-Ford algorithm
:param graph: dictionary representing weighted graph {vertex: [(neighbor, weight), ...]}
:param source: source vertex
:return: tuple (distances, predecessors) or None if negative cycle exists
"""
if source not in graph:
return None
# Get all vertices
vertices = set(graph.keys())
for vertex in graph:
for neighbor, _ in graph[vertex]:
vertices.add(neighbor)
# Initialize distances and predecessors
distances = {vertex: float('inf') for vertex in vertices}
predecessors = {vertex: None for vertex in vertices}
distances[source] = 0
# Relax edges repeatedly
for _ in range(len(vertices) - 1):
for vertex in graph:
if distances[vertex] != float('inf'):
for neighbor, weight in graph[vertex]:
if distances[vertex] + weight < distances[neighbor]:
distances[neighbor] = distances[vertex] + weight
predecessors[neighbor] = vertex
# Check for negative cycles
for vertex in graph:
if distances[vertex] != float('inf'):
for neighbor, weight in graph[vertex]:
if distances[vertex] + weight < distances[neighbor]:
return None # Negative cycle detected
return distances, predecessors
def bellman_ford_with_path(graph, source, target):
"""
Find shortest path from source to target using Bellman-Ford algorithm
:param graph: dictionary representing weighted graph
:param source: source vertex
:param target: target vertex
:return: tuple (distance, path) or None if no path or negative cycle
"""
result = bellman_ford(graph, source)
if result is None:
return None # Negative cycle
distances, predecessors = result
if target not in distances or distances[target] == float('inf'):
return None # No path to target
# Reconstruct path
path = []
current = target
while current is not None:
path.append(current)
current = predecessors[current]
path.reverse()
return distances[target], path
def detect_negative_cycle(graph):
"""
Detect if the graph contains a negative cycle
:param graph: dictionary representing weighted graph
:return: True if negative cycle exists, False otherwise
"""
if not graph:
return False
# Try Bellman-Ford from any vertex
source = next(iter(graph))
result = bellman_ford(graph, source)
return result is None
def bellman_ford_all_pairs(graph):
"""
Find shortest paths between all pairs of vertices
:param graph: dictionary representing weighted graph
:return: dictionary of distances or None if negative cycle exists
"""
vertices = set(graph.keys())
for vertex in graph:
for neighbor, _ in graph[vertex]:
vertices.add(neighbor)
all_distances = {}
for source in vertices:
result = bellman_ford(graph, source)
if result is None:
return None # Negative cycle
distances, _ = result
all_distances[source] = distances
return all_distances
def create_sample_graph():
"""
Create a sample weighted graph for testing
:return: dictionary representing a weighted graph
"""
return {
'A': [('B', -1), ('C', 4)],
'B': [('C', 3), ('D', 2), ('E', 2)],
'C': [],
'D': [('B', 1), ('C', 5)],
'E': [('D', -3)]
}
def create_negative_cycle_graph():
"""
Create a graph with a negative cycle for testing
:return: dictionary representing a graph with negative cycle
"""
return {
'A': [('B', 1)],
'B': [('C', -3)],
'C': [('D', 2)],
'D': [('B', -1)]
}
def print_distances(distances, source):
"""
Print the shortest distances from source to all vertices
:param distances: dictionary of distances
:param source: source vertex
:return: string representation of distances
"""
if not distances:
return "No distances to display"
result = f"Shortest distances from {source}:\n"
for vertex in sorted(distances.keys()):
if distances[vertex] == float('inf'):
result += f"{source} -> {vertex}: ∞\n"
else:
result += f"{source} -> {vertex}: {distances[vertex]}\n"
return result.strip()
def is_valid_graph(graph):
"""
Check if the graph representation is valid
:param graph: dictionary to validate
:return: True if valid, False otherwise
"""
if not isinstance(graph, dict):
return False
for vertex, edges in graph.items():
if not isinstance(edges, list):
return False
for edge in edges:
if not isinstance(edge, tuple) or len(edge) != 2:
return False
neighbor, weight = edge
if not isinstance(weight, (int, float)):
return False
return True
def time_complexities():
"""
Return information on time complexity
:return: string
"""
return "Best Case: O(V * E), Average Case: O(V * E), Worst Case: O(V * E)"
def get_code():
"""
Easily retrieve the source code of the bellman_ford function
:return: source code
"""
return inspect.getsource(bellman_ford)