Skip to content

Commit 3262296

Browse files
committed
add algorithms to detect cycle in graph
Signed-off-by: Pritam Singh <[email protected]>
1 parent b17c532 commit 3262296

File tree

3 files changed

+229
-0
lines changed

3 files changed

+229
-0
lines changed

DIRECTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@
143143
* [Tarjans Ssc](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/tarjans_ssc.rs)
144144
* [Topological Sort](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/topological_sort.rs)
145145
* [Two Satisfiability](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/two_satisfiability.rs)
146+
* [Cycle Detection](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/detect_cycle.rs)
146147
* [Lib](https://github.com/TheAlgorithms/Rust/blob/master/src/lib.rs)
147148
* Machine Learning
148149
* [Cholesky](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/cholesky.rs)

src/graph/detect_cycle.rs

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
use std::collections::{HashMap, HashSet, VecDeque};
2+
3+
use crate::data_structures::{graph::Graph, DirectedGraph, UndirectedGraph};
4+
5+
pub trait DetectCycle {
6+
fn detect_cycle_dfs(&self) -> bool;
7+
fn detect_cycle_bfs(&self) -> bool;
8+
}
9+
10+
// Helper function to detect cycle in an undirected graph using DFS graph traversal
11+
fn undirected_graph_detect_cycle_dfs<'a>(
12+
graph: &'a UndirectedGraph,
13+
visited_node: &mut HashSet<&'a String>,
14+
parent: &'a String,
15+
u: &'a String,
16+
) -> bool {
17+
visited_node.insert(u);
18+
for (v, _) in graph.adjacency_table().get(u).unwrap() {
19+
if v == parent {
20+
continue;
21+
}
22+
if visited_node.contains(v) || undirected_graph_detect_cycle_dfs(graph, visited_node, u, v)
23+
{
24+
return true;
25+
}
26+
}
27+
false
28+
}
29+
30+
// Helper function to detect cycle in an undirected graph using BFS graph traversal
31+
fn undirected_graph_detect_cycle_bfs<'a>(
32+
graph: &'a UndirectedGraph,
33+
visited_node: &mut HashSet<&'a String>,
34+
u: &'a String,
35+
) -> bool {
36+
visited_node.insert(u);
37+
38+
// Initialize the queue for BFS, storing (current node, parent node) tuples
39+
let mut queue = VecDeque::<(&String, &String)>::new();
40+
queue.push_back((u, u));
41+
42+
while let Some((u, parent)) = queue.pop_front() {
43+
for (v, _) in graph.adjacency_table().get(u).unwrap() {
44+
if v == parent {
45+
continue;
46+
}
47+
if visited_node.contains(v) {
48+
return true;
49+
}
50+
visited_node.insert(v);
51+
queue.push_back((v, u));
52+
}
53+
}
54+
false
55+
}
56+
57+
impl DetectCycle for UndirectedGraph {
58+
fn detect_cycle_dfs(&self) -> bool {
59+
let mut visited_node = HashSet::<&String>::new();
60+
let adj = self.adjacency_table();
61+
for u in adj.keys() {
62+
if !visited_node.contains(u)
63+
&& undirected_graph_detect_cycle_dfs(self, &mut visited_node, u, u)
64+
{
65+
return true;
66+
}
67+
}
68+
false
69+
}
70+
71+
fn detect_cycle_bfs(&self) -> bool {
72+
let mut visited_node = HashSet::<&String>::new();
73+
let adj = self.adjacency_table();
74+
for u in adj.keys() {
75+
if !visited_node.contains(u)
76+
&& undirected_graph_detect_cycle_bfs(self, &mut visited_node, u)
77+
{
78+
return true;
79+
}
80+
}
81+
false
82+
}
83+
}
84+
85+
// Helper function to detect cycle in a directed graph using DFS graph traversal
86+
fn directed_graph_detect_cycle_dfs<'a>(
87+
graph: &'a DirectedGraph,
88+
visited_node: &mut HashSet<&'a String>,
89+
in_stack_visited_node: &mut HashSet<&'a String>,
90+
parent: &'a String,
91+
u: &'a String,
92+
) -> bool {
93+
visited_node.insert(u);
94+
in_stack_visited_node.insert(u);
95+
for (v, _) in graph.adjacency_table().get(u).unwrap() {
96+
if v == parent {
97+
continue;
98+
}
99+
if visited_node.contains(v) && in_stack_visited_node.contains(v) {
100+
return true;
101+
}
102+
if !visited_node.contains(v)
103+
&& directed_graph_detect_cycle_dfs(graph, visited_node, in_stack_visited_node, u, v)
104+
{
105+
return true;
106+
}
107+
}
108+
in_stack_visited_node.remove(u);
109+
false
110+
}
111+
112+
impl DetectCycle for DirectedGraph {
113+
fn detect_cycle_dfs(&self) -> bool {
114+
let mut visited_node = HashSet::<&String>::new();
115+
let mut in_stack_visited_node = HashSet::<&String>::new();
116+
let adj = self.adjacency_table();
117+
for u in adj.keys() {
118+
if !visited_node.contains(u)
119+
&& directed_graph_detect_cycle_dfs(
120+
self,
121+
&mut visited_node,
122+
&mut in_stack_visited_node,
123+
u,
124+
u,
125+
)
126+
{
127+
return true;
128+
}
129+
}
130+
false
131+
}
132+
133+
// detect cycle in a the graph using Kahn's algorithm
134+
// https://www.geeksforgeeks.org/detect-cycle-in-a-directed-graph-using-bfs/
135+
fn detect_cycle_bfs(&self) -> bool {
136+
// Set 0 in-degree for each vertex
137+
let mut in_degree: HashMap<&String, usize> =
138+
self.adjacency_table().keys().map(|k| (k, 0)).collect();
139+
140+
// Calculate in-degree for each vertex
141+
for u in self.adjacency_table().keys() {
142+
for (v, _) in self.adjacency_table().get(u).unwrap() {
143+
*in_degree.get_mut(v).unwrap() += 1;
144+
}
145+
}
146+
// Initialize queue with vertex having 0 in-degree
147+
let mut queue: VecDeque<&String> = in_degree
148+
.iter()
149+
.filter(|(_, &degree)| degree == 0)
150+
.map(|(&k, _)| k)
151+
.collect();
152+
153+
let mut count = 0;
154+
while let Some(u) = queue.pop_front() {
155+
count += 1;
156+
for (v, _) in self.adjacency_table().get(u).unwrap() {
157+
in_degree.entry(v).and_modify(|d| {
158+
*d -= 1;
159+
if *d == 0 {
160+
queue.push_back(v);
161+
}
162+
});
163+
}
164+
}
165+
166+
// If count of processed vertices is not equal to the number of vertices,
167+
// the graph has a cycle
168+
count != self.adjacency_table().len()
169+
}
170+
}
171+
172+
#[cfg(test)]
173+
mod test {
174+
use crate::data_structures::{graph::Graph, DirectedGraph, UndirectedGraph};
175+
176+
use super::DetectCycle;
177+
178+
#[test]
179+
fn test_detect_cycle_in_undirected_graph() {
180+
let mut graph_with_cycle = UndirectedGraph::new();
181+
182+
graph_with_cycle.add_edge(("a", "b", 1));
183+
graph_with_cycle.add_edge(("a", "c", 1));
184+
graph_with_cycle.add_edge(("b", "c", 1));
185+
graph_with_cycle.add_edge(("b", "d", 1));
186+
graph_with_cycle.add_edge(("c", "d", 1));
187+
188+
assert!(graph_with_cycle.detect_cycle_dfs());
189+
assert!(graph_with_cycle.detect_cycle_bfs());
190+
191+
let mut graph_without_cycle = UndirectedGraph::new();
192+
193+
graph_without_cycle.add_edge(("a", "b", 1));
194+
graph_without_cycle.add_edge(("a", "c", 1));
195+
graph_without_cycle.add_edge(("b", "d", 1));
196+
graph_without_cycle.add_edge(("c", "e", 1));
197+
198+
assert!(!graph_without_cycle.detect_cycle_dfs());
199+
assert!(!graph_without_cycle.detect_cycle_bfs());
200+
}
201+
202+
#[test]
203+
fn test_detect_cycle_in_directed_graph() {
204+
let mut graph_with_cycle = DirectedGraph::new();
205+
206+
graph_with_cycle.add_edge(("b", "a", 1));
207+
graph_with_cycle.add_edge(("c", "a", 1));
208+
graph_with_cycle.add_edge(("b", "c", 1));
209+
graph_with_cycle.add_edge(("c", "d", 1));
210+
graph_with_cycle.add_edge(("d", "b", 1));
211+
212+
assert!(graph_with_cycle.detect_cycle_dfs());
213+
assert!(graph_with_cycle.detect_cycle_bfs());
214+
215+
let mut graph_without_cycle = DirectedGraph::new();
216+
217+
graph_without_cycle.add_edge(("b", "a", 1));
218+
graph_without_cycle.add_edge(("c", "a", 1));
219+
graph_without_cycle.add_edge(("b", "c", 1));
220+
graph_without_cycle.add_edge(("c", "d", 1));
221+
graph_without_cycle.add_edge(("b", "d", 1));
222+
223+
assert!(!graph_without_cycle.detect_cycle_dfs());
224+
assert!(!graph_without_cycle.detect_cycle_bfs());
225+
}
226+
}

src/graph/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod centroid_decomposition;
66
mod decremental_connectivity;
77
mod depth_first_search;
88
mod depth_first_search_tic_tac_toe;
9+
mod detect_cycle;
910
mod dijkstra;
1011
mod dinic_maxflow;
1112
mod disjoint_set_union;
@@ -33,6 +34,7 @@ pub use self::centroid_decomposition::CentroidDecomposition;
3334
pub use self::decremental_connectivity::DecrementalConnectivity;
3435
pub use self::depth_first_search::depth_first_search;
3536
pub use self::depth_first_search_tic_tac_toe::minimax;
37+
pub use self::detect_cycle::DetectCycle;
3638
pub use self::dijkstra::dijkstra;
3739
pub use self::dinic_maxflow::DinicMaxFlow;
3840
pub use self::disjoint_set_union::DisjointSetUnion;

0 commit comments

Comments
 (0)