leptos_helios/graph_features/
algorithms.rs1use super::{GraphEdge, GraphNode};
6use std::collections::{HashMap, HashSet, VecDeque};
7
8pub struct GraphAlgorithms;
10
11impl GraphAlgorithms {
12 pub fn find_connected_components(nodes: &[GraphNode], edges: &[GraphEdge]) -> Vec<Vec<String>> {
14 let mut visited = HashSet::new();
15 let mut components = Vec::new();
16
17 for node in nodes {
18 if !visited.contains(&node.id) {
19 let mut component = Vec::new();
20 Self::dfs_component(&node.id, nodes, edges, &mut visited, &mut component);
21 components.push(component);
22 }
23 }
24
25 components
26 }
27
28 fn dfs_component(
30 node_id: &str,
31 nodes: &[GraphNode],
32 edges: &[GraphEdge],
33 visited: &mut HashSet<String>,
34 component: &mut Vec<String>,
35 ) {
36 visited.insert(node_id.to_string());
37 component.push(node_id.to_string());
38
39 for edge in edges {
40 let neighbor = if edge.source == node_id {
41 Some(&edge.target)
42 } else if edge.target == node_id {
43 Some(&edge.source)
44 } else {
45 None
46 };
47
48 if let Some(neighbor_id) = neighbor {
49 if !visited.contains(neighbor_id) {
50 Self::dfs_component(neighbor_id, nodes, edges, visited, component);
51 }
52 }
53 }
54 }
55
56 pub fn dijkstra_shortest_path(
58 source: &str,
59 target: &str,
60 nodes: &[GraphNode],
61 edges: &[GraphEdge],
62 ) -> Option<Vec<String>> {
63 let mut distances: HashMap<String, f64> = nodes
64 .iter()
65 .map(|n| (n.id.clone(), f64::INFINITY))
66 .collect();
67 let mut previous: HashMap<String, Option<String>> =
68 nodes.iter().map(|n| (n.id.clone(), None)).collect();
69 let mut unvisited: HashSet<String> = nodes.iter().map(|n| n.id.clone()).collect();
70
71 distances.insert(source.to_string(), 0.0);
72
73 while !unvisited.is_empty() {
74 let current = unvisited
76 .iter()
77 .min_by(|a, b| {
78 distances
79 .get(*a)
80 .unwrap_or(&f64::INFINITY)
81 .partial_cmp(distances.get(*b).unwrap_or(&f64::INFINITY))
82 .unwrap()
83 })?
84 .clone();
85
86 if current == target {
87 break;
88 }
89
90 unvisited.remove(¤t);
91
92 for edge in edges {
94 let neighbor = if edge.source == current {
95 Some(&edge.target)
96 } else if edge.target == current {
97 Some(&edge.source)
98 } else {
99 None
100 };
101
102 if let Some(neighbor_id) = neighbor {
103 if unvisited.contains(neighbor_id) {
104 let alt = distances.get(¤t).unwrap_or(&f64::INFINITY) + edge.weight;
105 if alt < *distances.get(neighbor_id).unwrap_or(&f64::INFINITY) {
106 distances.insert(neighbor_id.clone(), alt);
107 previous.insert(neighbor_id.clone(), Some(current.clone()));
108 }
109 }
110 }
111 }
112 }
113
114 let mut path = Vec::new();
116 let mut current = target.to_string();
117
118 while let Some(prev) = previous.get(¤t)? {
119 path.push(current);
120 current = prev.clone();
121 }
122 path.push(source.to_string());
123 path.reverse();
124
125 Some(path)
126 }
127
128 pub fn kruskal_mst(nodes: &[GraphNode], edges: &[GraphEdge]) -> Vec<GraphEdge> {
130 let mut sorted_edges = edges.to_vec();
131 sorted_edges.sort_by(|a, b| a.weight.partial_cmp(&b.weight).unwrap());
132
133 let mut mst = Vec::new();
134 let mut parent: HashMap<String, String> =
135 nodes.iter().map(|n| (n.id.clone(), n.id.clone())).collect();
136
137 for edge in sorted_edges {
138 let root_source = Self::find_root(&edge.source, &parent);
139 let root_target = Self::find_root(&edge.target, &parent);
140
141 if root_source != root_target {
142 mst.push(edge.clone());
143 parent.insert(root_source, root_target);
144 }
145 }
146
147 mst
148 }
149
150 fn find_root(node: &str, parent: &HashMap<String, String>) -> String {
152 let mut current = node.to_string();
153 while parent.get(¤t) != Some(¤t) {
154 current = parent.get(¤t).unwrap().clone();
155 }
156 current
157 }
158
159 pub fn detect_cycle(nodes: &[GraphNode], edges: &[GraphEdge]) -> bool {
161 let mut visited = HashSet::new();
162 let mut rec_stack = HashSet::new();
163
164 for node in nodes {
165 if !visited.contains(&node.id) {
166 if Self::dfs_cycle(&node.id, nodes, edges, &mut visited, &mut rec_stack) {
167 return true;
168 }
169 }
170 }
171
172 false
173 }
174
175 fn dfs_cycle(
177 node_id: &str,
178 _nodes: &[GraphNode],
179 edges: &[GraphEdge],
180 visited: &mut HashSet<String>,
181 rec_stack: &mut HashSet<String>,
182 ) -> bool {
183 visited.insert(node_id.to_string());
184 rec_stack.insert(node_id.to_string());
185
186 for edge in edges {
187 let neighbor = if edge.source == node_id {
188 Some(&edge.target)
189 } else if edge.target == node_id {
190 Some(&edge.source)
191 } else {
192 None
193 };
194
195 if let Some(neighbor_id) = neighbor {
196 if !visited.contains(neighbor_id) {
197 if Self::dfs_cycle(neighbor_id, _nodes, edges, visited, rec_stack) {
198 return true;
199 }
200 } else if rec_stack.contains(neighbor_id) {
201 return true;
202 }
203 }
204 }
205
206 rec_stack.remove(node_id);
207 false
208 }
209
210 pub fn topological_sort(nodes: &[GraphNode], edges: &[GraphEdge]) -> Option<Vec<String>> {
212 let mut in_degree: HashMap<String, usize> =
213 nodes.iter().map(|n| (n.id.clone(), 0)).collect();
214
215 for edge in edges {
217 *in_degree.get_mut(&edge.target).unwrap() += 1;
218 }
219
220 let mut queue: VecDeque<String> = in_degree
222 .iter()
223 .filter(|(_, °ree)| degree == 0)
224 .map(|(node, _)| node.clone())
225 .collect();
226
227 let mut result = Vec::new();
228
229 while let Some(current) = queue.pop_front() {
230 result.push(current.clone());
231
232 for edge in edges {
234 if edge.source == current {
235 let neighbor = &edge.target;
236 if let Some(degree) = in_degree.get_mut(neighbor) {
237 *degree -= 1;
238 if *degree == 0 {
239 queue.push_back(neighbor.clone());
240 }
241 }
242 }
243 }
244 }
245
246 if result.len() == nodes.len() {
248 Some(result)
249 } else {
250 None }
252 }
253
254 pub fn tarjan_scc(nodes: &[GraphNode], edges: &[GraphEdge]) -> Vec<Vec<String>> {
256 let mut index = 0;
257 let mut stack = Vec::new();
258 let mut indices: HashMap<String, usize> = HashMap::new();
259 let mut lowlinks: HashMap<String, usize> = HashMap::new();
260 let mut on_stack: HashSet<String> = HashSet::new();
261 let mut sccs = Vec::new();
262
263 for node in nodes {
264 if !indices.contains_key(&node.id) {
265 Self::tarjan_visit(
266 &node.id,
267 nodes,
268 edges,
269 &mut index,
270 &mut stack,
271 &mut indices,
272 &mut lowlinks,
273 &mut on_stack,
274 &mut sccs,
275 );
276 }
277 }
278
279 sccs
280 }
281
282 fn tarjan_visit(
284 node_id: &str,
285 nodes: &[GraphNode],
286 edges: &[GraphEdge],
287 index: &mut usize,
288 stack: &mut Vec<String>,
289 indices: &mut HashMap<String, usize>,
290 lowlinks: &mut HashMap<String, usize>,
291 on_stack: &mut HashSet<String>,
292 sccs: &mut Vec<Vec<String>>,
293 ) {
294 indices.insert(node_id.to_string(), *index);
295 lowlinks.insert(node_id.to_string(), *index);
296 *index += 1;
297 stack.push(node_id.to_string());
298 on_stack.insert(node_id.to_string());
299
300 for edge in edges {
302 let neighbor = if edge.source == node_id {
303 Some(&edge.target)
304 } else if edge.target == node_id {
305 Some(&edge.source)
306 } else {
307 None
308 };
309
310 if let Some(neighbor_id) = neighbor {
311 if !indices.contains_key(neighbor_id) {
312 Self::tarjan_visit(
313 neighbor_id,
314 nodes,
315 edges,
316 index,
317 stack,
318 indices,
319 lowlinks,
320 on_stack,
321 sccs,
322 );
323 let lowlink = *lowlinks
324 .get(node_id)
325 .unwrap()
326 .min(lowlinks.get(neighbor_id).unwrap());
327 lowlinks.insert(node_id.to_string(), lowlink);
328 } else if on_stack.contains(neighbor_id) {
329 let lowlink = *lowlinks
330 .get(node_id)
331 .unwrap()
332 .min(indices.get(neighbor_id).unwrap());
333 lowlinks.insert(node_id.to_string(), lowlink);
334 }
335 }
336 }
337
338 if lowlinks.get(node_id) == indices.get(node_id) {
340 let mut scc = Vec::new();
341 loop {
342 let w = stack.pop().unwrap();
343 on_stack.remove(&w);
344 scc.push(w.clone());
345 if w == *node_id {
346 break;
347 }
348 }
349 sccs.push(scc);
350 }
351 }
352}