Skip to content

Commit b05af0a

Browse files
[pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
1 parent 8066d8f commit b05af0a

File tree

4 files changed

+308
-304
lines changed

4 files changed

+308
-304
lines changed

graphs/bellman_ford.py

Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -16,169 +16,169 @@ class Graph:
1616
"""
1717
A lightweight graph class for algorithms that don't have access to the main Graph class.
1818
"""
19-
19+
2020
def __init__(self):
2121
self.adjacency_list = defaultdict(list)
2222
self.vertices = set()
23-
23+
2424
def add_edge(self, source: Any, destination: Any, weight: float) -> None:
2525
self.adjacency_list[source].append((destination, weight))
2626
self.vertices.add(source)
2727
self.vertices.add(destination)
28-
28+
2929
def get_neighbors(self, vertex: Any) -> List[Tuple[Any, float]]:
3030
return self.adjacency_list.get(vertex, [])
31-
31+
3232
def get_vertices(self) -> set:
3333
return self.vertices.copy()
34-
34+
3535
def has_vertex(self, vertex: Any) -> bool:
3636
return vertex in self.vertices
37-
37+
3838
def get_vertex_count(self) -> int:
3939
return len(self.vertices)
4040

4141

4242
class BellmanFord:
4343
"""
4444
Bellman-Ford algorithm implementation for single-source shortest path.
45-
45+
4646
This algorithm can handle graphs with negative edge weights and can detect
4747
negative cycles. It's particularly useful as a preprocessing step in
4848
Johnson's algorithm.
4949
"""
50-
50+
5151
def __init__(self, graph):
5252
"""
5353
Initialize the Bellman-Ford algorithm with a graph.
54-
54+
5555
Args:
5656
graph: The weighted directed graph to process
5757
"""
5858
self.graph = graph
5959
self.distances = {}
6060
self.predecessors = {}
61-
61+
6262
def find_shortest_paths(self, start_vertex: Any) -> Optional[Dict[Any, float]]:
6363
"""
6464
Find shortest paths from start_vertex to all other vertices.
65-
65+
6666
Args:
6767
start_vertex: The source vertex to start from
68-
68+
6969
Returns:
7070
Dictionary of vertex -> shortest distance, or None if negative cycle exists
7171
"""
7272
if not self.graph.has_vertex(start_vertex):
7373
raise ValueError(f"Start vertex {start_vertex} not found in graph")
74-
74+
7575
# Initialize distances
76-
self.distances = {vertex: float('inf') for vertex in self.graph.get_vertices()}
76+
self.distances = {vertex: float("inf") for vertex in self.graph.get_vertices()}
7777
self.distances[start_vertex] = 0
7878
self.predecessors = {vertex: None for vertex in self.graph.get_vertices()}
79-
79+
8080
# Relax edges V-1 times
8181
vertex_count = self.graph.get_vertex_count()
82-
82+
8383
for iteration in range(vertex_count - 1):
8484
updated = False
85-
85+
8686
for vertex in self.graph.get_vertices():
87-
if self.distances[vertex] != float('inf'):
87+
if self.distances[vertex] != float("inf"):
8888
for neighbor, weight in self.graph.get_neighbors(vertex):
8989
new_distance = self.distances[vertex] + weight
90-
90+
9191
if new_distance < self.distances[neighbor]:
9292
self.distances[neighbor] = new_distance
9393
self.predecessors[neighbor] = vertex
9494
updated = True
95-
95+
9696
# Early termination if no updates in this iteration
9797
if not updated:
9898
break
99-
99+
100100
# Check for negative cycles
101101
if self._has_negative_cycle():
102102
return None
103-
103+
104104
return self.distances.copy()
105-
105+
106106
def _has_negative_cycle(self) -> bool:
107107
"""
108108
Check if the graph contains a negative cycle.
109-
109+
110110
Returns:
111111
True if negative cycle exists, False otherwise
112112
"""
113113
for vertex in self.graph.get_vertices():
114-
if self.distances[vertex] != float('inf'):
114+
if self.distances[vertex] != float("inf"):
115115
for neighbor, weight in self.graph.get_neighbors(vertex):
116116
if self.distances[vertex] + weight < self.distances[neighbor]:
117117
return True
118118
return False
119-
119+
120120
def get_path(self, start_vertex: Any, end_vertex: Any) -> Optional[List[Any]]:
121121
"""
122122
Get the shortest path from start_vertex to end_vertex.
123-
123+
124124
Args:
125125
start_vertex: Source vertex
126126
end_vertex: Destination vertex
127-
127+
128128
Returns:
129129
List of vertices representing the path, or None if no path exists
130130
"""
131131
if not self.distances or end_vertex not in self.distances:
132132
return None
133-
134-
if self.distances[end_vertex] == float('inf'):
133+
134+
if self.distances[end_vertex] == float("inf"):
135135
return None
136-
136+
137137
path = []
138138
current = end_vertex
139-
139+
140140
while current is not None:
141141
path.append(current)
142142
current = self.predecessors.get(current)
143-
143+
144144
path.reverse()
145-
145+
146146
# Verify the path starts with start_vertex
147147
if path[0] != start_vertex:
148148
return None
149-
149+
150150
return path
151-
151+
152152
def get_distance(self, vertex: Any) -> float:
153153
"""
154154
Get the shortest distance to a specific vertex.
155-
155+
156156
Args:
157157
vertex: The target vertex
158-
158+
159159
Returns:
160160
Shortest distance to the vertex
161161
"""
162-
return self.distances.get(vertex, float('inf'))
163-
162+
return self.distances.get(vertex, float("inf"))
163+
164164
@staticmethod
165165
def detect_negative_cycle(graph) -> bool:
166166
"""
167167
Static method to detect if a graph contains a negative cycle.
168-
168+
169169
Args:
170170
graph: The graph to check
171-
171+
172172
Returns:
173173
True if negative cycle exists, False otherwise
174174
"""
175175
vertices = graph.get_vertices()
176176
if not vertices:
177177
return False
178-
178+
179179
# Pick any vertex as start
180180
start_vertex = next(iter(vertices))
181181
bf = BellmanFord(graph)
182182
result = bf.find_shortest_paths(start_vertex)
183-
183+
184184
return result is None

0 commit comments

Comments
 (0)