advanced_algorithms/graph/
bellman_ford.rs1use crate::graph::Graph;
24use crate::{AlgorithmError, Result};
25
26#[derive(Debug, Clone)]
28pub struct ShortestPathResult {
29 pub distance: Vec<f64>,
31 pub previous: Vec<Option<usize>>,
33 pub source: usize,
35}
36
37impl ShortestPathResult {
38 pub fn path_to(&self, target: usize) -> Option<Vec<usize>> {
40 if self.distance[target].is_infinite() {
41 return None;
42 }
43
44 let mut path = Vec::new();
45 let mut current = target;
46
47 while current != self.source {
48 path.push(current);
49 current = self.previous[current]?;
50 }
51
52 path.push(self.source);
53 path.reverse();
54
55 Some(path)
56 }
57}
58
59pub fn shortest_path(graph: &Graph, source: usize) -> Result<ShortestPathResult> {
74 let n = graph.n_nodes;
75
76 let mut distance = vec![f64::INFINITY; n];
77 let mut previous = vec![None; n];
78
79 distance[source] = 0.0;
80
81 for _ in 0..n - 1 {
83 let mut changed = false;
84
85 for u in 0..n {
86 if distance[u].is_infinite() {
87 continue;
88 }
89
90 for &(v, weight) in graph.neighbors(u) {
91 let new_distance = distance[u] + weight;
92
93 if new_distance < distance[v] {
94 distance[v] = new_distance;
95 previous[v] = Some(u);
96 changed = true;
97 }
98 }
99 }
100
101 if !changed {
103 break;
104 }
105 }
106
107 for u in 0..n {
109 if distance[u].is_infinite() {
110 continue;
111 }
112
113 for &(v, weight) in graph.neighbors(u) {
114 if distance[u] + weight < distance[v] {
115 return Err(AlgorithmError::InvalidInput(
116 "Graph contains a negative cycle".to_string()
117 ));
118 }
119 }
120 }
121
122 Ok(ShortestPathResult {
123 distance,
124 previous,
125 source,
126 })
127}
128
129pub fn has_negative_cycle(graph: &Graph) -> bool {
139 let n = graph.n_nodes;
140 let mut distance = vec![0.0; n];
141
142 for _ in 0..n - 1 {
144 for u in 0..n {
145 for &(v, weight) in graph.neighbors(u) {
146 let new_distance = distance[u] + weight;
147 if new_distance < distance[v] {
148 distance[v] = new_distance;
149 }
150 }
151 }
152 }
153
154 for u in 0..n {
156 for &(v, weight) in graph.neighbors(u) {
157 if distance[u] + weight < distance[v] {
158 return true;
159 }
160 }
161 }
162
163 false
164}
165
166pub fn find_negative_cycle(graph: &Graph) -> Option<Vec<usize>> {
172 let n = graph.n_nodes;
173 let mut distance = vec![0.0; n];
174 let mut previous = vec![None; n];
175
176 let mut last_updated = None;
178
179 for _iteration in 0..n {
180 let mut changed = false;
181
182 for u in 0..n {
183 for &(v, weight) in graph.neighbors(u) {
184 let new_distance = distance[u] + weight;
185
186 if new_distance < distance[v] {
187 distance[v] = new_distance;
188 previous[v] = Some(u);
189 changed = true;
190 last_updated = Some(v);
191 }
192 }
193 }
194
195 if !changed {
196 return None;
197 }
198 }
199
200 if let Some(mut node) = last_updated {
202 for _ in 0..n {
204 node = previous[node]?;
205 }
206
207 let mut cycle = vec![node];
209 let mut current = previous[node]?;
210
211 while current != node {
212 cycle.push(current);
213 current = previous[current]?;
214 }
215
216 cycle.reverse();
217 return Some(cycle);
218 }
219
220 None
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 #[test]
228 fn test_positive_weights() {
229 let mut graph = Graph::new(4);
230 graph.add_edge(0, 1, 1.0);
231 graph.add_edge(1, 2, 2.0);
232 graph.add_edge(2, 3, 3.0);
233
234 let result = shortest_path(&graph, 0).unwrap();
235
236 assert_eq!(result.distance[3], 6.0);
237 }
238
239 #[test]
240 fn test_negative_weights() {
241 let mut graph = Graph::new(4);
242 graph.add_edge(0, 1, 1.0);
243 graph.add_edge(1, 2, -3.0);
244 graph.add_edge(2, 3, 1.0);
245 graph.add_edge(0, 3, 5.0);
246
247 let result = shortest_path(&graph, 0).unwrap();
248
249 assert_eq!(result.distance[3], -1.0); }
251
252 #[test]
253 fn test_negative_cycle() {
254 let mut graph = Graph::new(3);
255 graph.add_edge(0, 1, 1.0);
256 graph.add_edge(1, 2, -3.0);
257 graph.add_edge(2, 0, 1.0); let result = shortest_path(&graph, 0);
260 assert!(result.is_err());
261
262 assert!(has_negative_cycle(&graph));
263
264 let cycle = find_negative_cycle(&graph);
265 assert!(cycle.is_some());
266 }
267
268 #[test]
269 fn test_disconnected() {
270 let mut graph = Graph::new(3);
271 graph.add_edge(0, 1, 1.0);
272
273 let result = shortest_path(&graph, 0).unwrap();
274
275 assert!(result.distance[2].is_infinite());
276 }
277}