1pub fn detect_cycle_undirected(n: usize, edges: &[(usize, usize)]) -> Option<Vec<usize>> {
33 fn dfs(
34 curr: usize,
35 prev: usize,
36 g: &[Vec<(usize, usize)>],
37 seen: &mut Vec<bool>,
38 parent: &mut Vec<(usize, usize)>,
39 ) -> Option<(usize, usize)> {
40 seen[curr] = true;
41 for &(nxt, idx) in &g[curr] {
42 if nxt == prev {
43 continue;
44 }
45 if seen[nxt] {
46 return Some((nxt, curr));
47 }
48 parent[nxt] = (curr, idx);
49 if let Some((start, end)) = dfs(nxt, curr, g, seen, parent) {
50 return Some((start, end));
51 }
52 }
53 None
54 }
55
56 let mut g = vec![vec![]; n];
57 for (idx, &(u, v)) in edges.iter().enumerate() {
58 g[u].push((v, idx));
59 g[v].push((u, idx));
60 }
61 let mut seen = vec![false; n];
62 let mut parent = vec![(!0, !0); n];
63
64 for v in 0..n {
65 if seen[v] {
66 continue;
67 }
68 if let Some((cycle_start, cycle_end)) = dfs(v, !0, &g, &mut seen, &mut parent) {
69 let mut cycle = Vec::new();
70 {
71 let mut curr = cycle_end;
72 while curr != cycle_start {
73 let (par, idx) = parent[curr];
74 cycle.push(idx);
75 curr = par;
76 }
77 }
78 for (idx, &(u, v)) in edges.iter().enumerate() {
80 if (u, v) == (cycle_start, cycle_end) || (u, v) == (cycle_end, cycle_start) {
81 cycle.push(idx);
82 return Some(cycle);
83 }
84 }
85 unreachable!();
86 }
87 }
88 None
89}
90
91pub fn detect_cycle_directed(n: usize, edges: &[(usize, usize)]) -> Option<Vec<usize>> {
114 fn dfs(
115 curr: usize,
116 g: &[Vec<(usize, usize)>],
117 seen: &mut Vec<bool>,
118 on_path: &mut Vec<bool>,
119 ) -> Option<(usize, Vec<usize>, bool)> {
120 seen[curr] = true;
121 on_path[curr] = true;
122 for &(nxt, idx) in &g[curr] {
123 if on_path[nxt] {
124 assert!(seen[nxt]);
125 return Some((nxt, vec![idx], true));
126 }
127 if seen[nxt] {
128 continue;
129 }
130 if let Some((start_node, mut cycle, in_cycle)) = dfs(nxt, g, seen, on_path) {
131 return if in_cycle {
132 cycle.push(idx);
133 if curr == start_node {
134 cycle.reverse();
135 Some((start_node, cycle, false))
136 } else {
137 Some((start_node, cycle, true))
138 }
139 } else {
140 Some((start_node, cycle, false))
141 };
142 }
143 }
144 on_path[curr] = false;
145 None
146 }
147
148 let mut g = vec![vec![]; n];
149 for (idx, &(u, v)) in edges.iter().enumerate() {
150 g[u].push((v, idx));
151 }
152 let mut seen = vec![false; n];
153 let mut on_path = vec![false; n];
154 for v in 0..n {
155 if seen[v] {
156 continue;
157 }
158 if let Some((_, cycle, in_cycle)) = dfs(v, &g, &mut seen, &mut on_path) {
159 assert!(!in_cycle);
160 return Some(cycle);
161 }
162 }
163 None
164}
165
166#[cfg(test)]
167mod tests {
168 use crate::detect_cycle_directed;
169
170 #[test]
171 fn test_directed_triangle() {
172 let cycle = detect_cycle_directed(3, &[(0, 2), (2, 1), (1, 0)]);
173 assert_eq!(cycle, Some(vec![0, 1, 2]));
174 }
175
176 #[test]
177 fn test_directed_v() {
178 let cycle = detect_cycle_directed(3, &[(0, 2), (0, 1)]);
179 assert_eq!(cycle, None);
180 }
181}