rust_algorithms/graph/traversal.rs
1//! Grafni aylanib chiqish: BFS, DFS va ular ustiga qurilgan algoritmlar.
2
3use super::Graph;
4use std::collections::VecDeque;
5
6/// **BFS** (Breadth-First Search) — kenglikka qidiruv: to'lqin kabi tarqaladi.
7///
8/// **G'oya:** navbat (queue) ishlatamiz. Avval boshlang'ich tugunning barcha
9/// qo'shnilarini ko'ramiz, keyin ularning qo'shnilarini — ya'ni **qavat-qavat**.
10///
11/// Shu sababli og'irliksiz grafda BFS **eng qisqa yo'lni** topadi.
12///
13/// - **Time:** O(V + E), **Space:** O(V).
14///
15/// Qaytadi: tugunlarni ko'rilgan tartibda.
16///
17/// # Misol
18/// ```
19/// use rust_algorithms::graph::{Graph, bfs};
20///
21/// // 0 — 1 — 3
22/// // |
23/// // 2
24/// let mut g = Graph::new_undirected(4);
25/// g.add_unweighted_edge(0, 1);
26/// g.add_unweighted_edge(0, 2);
27/// g.add_unweighted_edge(1, 3);
28///
29/// assert_eq!(bfs(&g, 0), vec![0, 1, 2, 3]);
30/// ```
31pub fn bfs(g: &Graph, start: usize) -> Vec<usize> {
32 let mut korilgan = vec![false; g.node_count()];
33 let mut tartib = Vec::new();
34 let mut navbat = VecDeque::new();
35
36 korilgan[start] = true;
37 navbat.push_back(start);
38
39 while let Some(u) = navbat.pop_front() {
40 tartib.push(u);
41 for e in g.neighbors(u) {
42 if !korilgan[e.to] {
43 korilgan[e.to] = true; // navbatga qo'yishda belgilaymiz — takror bo'lmaydi
44 navbat.push_back(e.to);
45 }
46 }
47 }
48 tartib
49}
50
51/// **DFS** (Depth-First Search) — chuqurlikka qidiruv: bir yo'ldan oxirigacha borib,
52/// keyin orqaga qaytadi (backtrack).
53///
54/// Bu yerda **stack** bilan iterativ implementatsiya — chuqur graflarda ham
55/// stack overflow bo'lmaydi.
56///
57/// - **Time:** O(V + E), **Space:** O(V).
58///
59/// # Misol
60/// ```
61/// use rust_algorithms::graph::{Graph, dfs};
62///
63/// let mut g = Graph::new_undirected(4);
64/// g.add_unweighted_edge(0, 1);
65/// g.add_unweighted_edge(0, 2);
66/// g.add_unweighted_edge(1, 3);
67///
68/// let tartib = dfs(&g, 0);
69/// assert_eq!(tartib.len(), 4);
70/// assert_eq!(tartib[0], 0);
71/// ```
72pub fn dfs(g: &Graph, start: usize) -> Vec<usize> {
73 let mut korilgan = vec![false; g.node_count()];
74 let mut tartib = Vec::new();
75 let mut stack = vec![start];
76
77 while let Some(u) = stack.pop() {
78 if korilgan[u] {
79 continue;
80 }
81 korilgan[u] = true;
82 tartib.push(u);
83 // teskari tartibda qo'shamiz — natija rekursiv DFS bilan mos tushadi
84 for e in g.neighbors(u).iter().rev() {
85 if !korilgan[e.to] {
86 stack.push(e.to);
87 }
88 }
89 }
90 tartib
91}
92
93/// DFS ning rekursiv ko'rinishi — g'oyani ko'rsatish uchun.
94///
95/// # Misol
96/// ```
97/// use rust_algorithms::graph::{Graph, dfs, dfs_recursive};
98///
99/// let mut g = Graph::new_undirected(5);
100/// for (a, b) in [(0, 1), (1, 2), (0, 3), (3, 4)] {
101/// g.add_unweighted_edge(a, b);
102/// }
103/// assert_eq!(dfs_recursive(&g, 0), dfs(&g, 0));
104/// ```
105pub fn dfs_recursive(g: &Graph, start: usize) -> Vec<usize> {
106 fn go(g: &Graph, u: usize, korilgan: &mut [bool], tartib: &mut Vec<usize>) {
107 korilgan[u] = true;
108 tartib.push(u);
109 for e in g.neighbors(u) {
110 if !korilgan[e.to] {
111 go(g, e.to, korilgan, tartib);
112 }
113 }
114 }
115 let mut korilgan = vec![false; g.node_count()];
116 let mut tartib = Vec::new();
117 go(g, start, &mut korilgan, &mut tartib);
118 tartib
119}
120
121/// Bog'liq komponentalar: bir-biriga yetib bo'ladigan tugunlar guruhlari.
122///
123/// Yo'naltirilmagan graflar uchun. Har bir guruh alohida vektor.
124///
125/// - **Time:** O(V + E).
126///
127/// # Misol
128/// ```
129/// use rust_algorithms::graph::{Graph, connected_components};
130///
131/// let mut g = Graph::new_undirected(5);
132/// g.add_unweighted_edge(0, 1);
133/// g.add_unweighted_edge(3, 4);
134/// // 2 — yolg'iz tugun
135///
136/// let k = connected_components(&g);
137/// assert_eq!(k.len(), 3);
138/// assert_eq!(k[0], vec![0, 1]);
139/// assert_eq!(k[1], vec![2]);
140/// assert_eq!(k[2], vec![3, 4]);
141/// ```
142pub fn connected_components(g: &Graph) -> Vec<Vec<usize>> {
143 let n = g.node_count();
144 let mut korilgan = vec![false; n];
145 let mut natija = Vec::new();
146
147 for start in 0..n {
148 if korilgan[start] {
149 continue;
150 }
151 let mut komponenta = Vec::new();
152 let mut stack = vec![start];
153 korilgan[start] = true;
154 while let Some(u) = stack.pop() {
155 komponenta.push(u);
156 for e in g.neighbors(u) {
157 if !korilgan[e.to] {
158 korilgan[e.to] = true;
159 stack.push(e.to);
160 }
161 }
162 }
163 komponenta.sort_unstable();
164 natija.push(komponenta);
165 }
166 natija
167}
168
169/// Grafda sikl bormi?
170///
171/// - **Yo'naltirilmagan grafda:** DFS paytida "ota bo'lmagan ko'rilgan qo'shni" topilsa — sikl.
172/// - **Yo'naltirilgan grafda:** hozirgi rekursiya yo'lida turgan tugunga qaytsak — sikl
173/// (bu "orqaga qaytuvchi qirra" / back edge deyiladi).
174///
175/// - **Time:** O(V + E).
176///
177/// # Misol
178/// ```
179/// use rust_algorithms::graph::{Graph, has_cycle};
180///
181/// let mut daraxt = Graph::new_undirected(3);
182/// daraxt.add_unweighted_edge(0, 1);
183/// daraxt.add_unweighted_edge(1, 2);
184/// assert!(!has_cycle(&daraxt));
185///
186/// daraxt.add_unweighted_edge(2, 0); // uchburchak hosil bo'ldi
187/// assert!(has_cycle(&daraxt));
188/// ```
189pub fn has_cycle(g: &Graph) -> bool {
190 let n = g.node_count();
191 if g.is_directed() {
192 // 0 = ko'rilmagan, 1 = hozir yo'lda, 2 = tugagan
193 let mut holat = vec![0u8; n];
194 fn go(g: &Graph, u: usize, holat: &mut [u8]) -> bool {
195 holat[u] = 1;
196 for e in g.neighbors(u) {
197 if holat[e.to] == 1 {
198 return true; // yo'ldagi tugunga qaytdik → sikl
199 }
200 if holat[e.to] == 0 && go(g, e.to, holat) {
201 return true;
202 }
203 }
204 holat[u] = 2;
205 false
206 }
207 (0..n).any(|u| holat[u] == 0 && go(g, u, &mut holat))
208 } else {
209 let mut korilgan = vec![false; n];
210 fn go(g: &Graph, u: usize, ota: Option<usize>, korilgan: &mut [bool]) -> bool {
211 korilgan[u] = true;
212 for e in g.neighbors(u) {
213 if e.to == u {
214 return true; // sirtmoq
215 }
216 if !korilgan[e.to] {
217 if go(g, e.to, Some(u), korilgan) {
218 return true;
219 }
220 } else if Some(e.to) != ota {
221 return true;
222 }
223 }
224 false
225 }
226 (0..n).any(|u| !korilgan[u] && go(g, u, None, &mut korilgan))
227 }
228}
229
230/// Topologik saralash — yo'naltirilgan asiklik grafda (DAG) bog'liqliklar tartibi.
231///
232/// **Masala:** "A ni B dan oldin bajarish kerak" turidagi cheklovlar berilgan.
233/// Qaysi tartibda bajarsak, hech bir cheklov buzilmaydi?
234///
235/// **Kahn algoritmi:** kiruvchi qirrasi yo'q (`in_degree == 0`) tugunlarni navbatga
236/// solamiz; birini olganimizda uning qirralarini "o'chirib", qo'shnilarining
237/// `in_degree` sini kamaytiramiz.
238///
239/// Sikl bo'lsa (bog'liqliklar aylanma) — `None`.
240///
241/// - **Time:** O(V + E).
242///
243/// # Misol: darslarni qaysi tartibda o'qish kerak
244/// ```
245/// use rust_algorithms::graph::{Graph, topological_sort};
246///
247/// // 0: Matematika → 1: Algoritmlar → 3: ML
248/// // 2: Rust → 3
249/// let mut g = Graph::new_directed(4);
250/// g.add_unweighted_edge(0, 1);
251/// g.add_unweighted_edge(1, 3);
252/// g.add_unweighted_edge(2, 3);
253///
254/// let tartib = topological_sort(&g).unwrap();
255/// let orin = |x: usize| tartib.iter().position(|&y| y == x).unwrap();
256/// assert!(orin(0) < orin(1) && orin(1) < orin(3) && orin(2) < orin(3));
257/// ```
258pub fn topological_sort(g: &Graph) -> Option<Vec<usize>> {
259 let n = g.node_count();
260 let mut kiruvchi = vec![0usize; n];
261 for u in 0..n {
262 for e in g.neighbors(u) {
263 kiruvchi[e.to] += 1;
264 }
265 }
266
267 let mut navbat: VecDeque<usize> = (0..n).filter(|&u| kiruvchi[u] == 0).collect();
268 let mut tartib = Vec::with_capacity(n);
269
270 while let Some(u) = navbat.pop_front() {
271 tartib.push(u);
272 for e in g.neighbors(u) {
273 kiruvchi[e.to] -= 1;
274 if kiruvchi[e.to] == 0 {
275 navbat.push_back(e.to);
276 }
277 }
278 }
279
280 if tartib.len() == n {
281 Some(tartib)
282 } else {
283 None // sikl bor
284 }
285}
286
287/// Graf ikki bo'lakli (bipartite) mi? — tugunlarni 2 rangga bo'yash mumkinmi,
288/// shundayki qo'shnilar har doim turli rangda bo'lsin.
289///
290/// **Usul:** BFS bilan yurib, har bir qavatni navbatma-navbat bo'yaymiz.
291/// Qo'shni bir xil rangga tushsa — bo'lmaydi.
292///
293/// **Qayerda kerak:** "ikki guruhga bo'lish", "jadval to'qnashuvlari",
294/// "toq uzunlikdagi sikl bormi?" masalalari.
295///
296/// # Misol
297/// ```
298/// use rust_algorithms::graph::{Graph, is_bipartite};
299///
300/// let mut kvadrat = Graph::new_undirected(4); // 4 uzunlikdagi sikl — juft
301/// for (a, b) in [(0, 1), (1, 2), (2, 3), (3, 0)] {
302/// kvadrat.add_unweighted_edge(a, b);
303/// }
304/// assert!(is_bipartite(&kvadrat).is_some());
305///
306/// let mut uchburchak = Graph::new_undirected(3); // toq sikl
307/// for (a, b) in [(0, 1), (1, 2), (2, 0)] {
308/// uchburchak.add_unweighted_edge(a, b);
309/// }
310/// assert!(is_bipartite(&uchburchak).is_none());
311/// ```
312pub fn is_bipartite(g: &Graph) -> Option<Vec<u8>> {
313 let n = g.node_count();
314 let mut rang = vec![u8::MAX; n]; // MAX = bo'yalmagan
315
316 for start in 0..n {
317 if rang[start] != u8::MAX {
318 continue;
319 }
320 rang[start] = 0;
321 let mut navbat = VecDeque::from([start]);
322 while let Some(u) = navbat.pop_front() {
323 for e in g.neighbors(u) {
324 if rang[e.to] == u8::MAX {
325 rang[e.to] = 1 - rang[u];
326 navbat.push_back(e.to);
327 } else if rang[e.to] == rang[u] {
328 return None;
329 }
330 }
331 }
332 }
333 Some(rang)
334}
335
336#[cfg(test)]
337mod tests {
338 use super::*;
339
340 fn namuna() -> Graph {
341 // 0 — 1 — 3
342 // | |
343 // 2 — 4
344 let mut g = Graph::new_undirected(5);
345 for (a, b) in [(0, 1), (0, 2), (1, 3), (1, 4), (2, 4)] {
346 g.add_unweighted_edge(a, b);
347 }
348 g
349 }
350
351 #[test]
352 fn bfs_qavat_boyicha() {
353 let g = namuna();
354 assert_eq!(bfs(&g, 0), vec![0, 1, 2, 3, 4]);
355 }
356
357 #[test]
358 fn dfs_chuqurlikka() {
359 let g = namuna();
360 let t = dfs(&g, 0);
361 assert_eq!(t.len(), 5);
362 assert_eq!(t, dfs_recursive(&g, 0));
363 }
364
365 #[test]
366 fn komponentalar() {
367 let mut g = Graph::new_undirected(7);
368 g.add_unweighted_edge(0, 1);
369 g.add_unweighted_edge(1, 2);
370 g.add_unweighted_edge(4, 5);
371 let k = connected_components(&g);
372 assert_eq!(k, vec![vec![0, 1, 2], vec![3], vec![4, 5], vec![6]]);
373 }
374
375 #[test]
376 fn sikl_yonaltirilmagan() {
377 let mut daraxt = Graph::new_undirected(4);
378 daraxt.add_unweighted_edge(0, 1);
379 daraxt.add_unweighted_edge(1, 2);
380 daraxt.add_unweighted_edge(1, 3);
381 assert!(!has_cycle(&daraxt));
382
383 assert!(has_cycle(&namuna()));
384 }
385
386 #[test]
387 fn sikl_yonaltirilgan() {
388 let mut dag = Graph::new_directed(3);
389 dag.add_unweighted_edge(0, 1);
390 dag.add_unweighted_edge(1, 2);
391 dag.add_unweighted_edge(0, 2);
392 assert!(!has_cycle(&dag));
393
394 dag.add_unweighted_edge(2, 0);
395 assert!(has_cycle(&dag));
396 }
397
398 #[test]
399 fn topologik_saralash() {
400 let mut g = Graph::new_directed(6);
401 for (a, b) in [(5, 2), (5, 0), (4, 0), (4, 1), (2, 3), (3, 1)] {
402 g.add_unweighted_edge(a, b);
403 }
404 let t = topological_sort(&g).unwrap();
405 assert_eq!(t.len(), 6);
406 // har bir qirra uchun: from tartibda to dan oldin turishi kerak
407 for (from, to, _) in g.all_edges() {
408 let i = t.iter().position(|&x| x == from).unwrap();
409 let j = t.iter().position(|&x| x == to).unwrap();
410 assert!(i < j, "{from} → {to} tartibi buzilgan");
411 }
412 }
413
414 #[test]
415 fn siklli_grafda_topologik_saralash_yoq() {
416 let mut g = Graph::new_directed(2);
417 g.add_unweighted_edge(0, 1);
418 g.add_unweighted_edge(1, 0);
419 assert!(topological_sort(&g).is_none());
420 }
421
422 #[test]
423 fn ikki_bolaklilik() {
424 let mut g = Graph::new_undirected(6);
425 // 0,2,4 — bir guruh; 1,3,5 — ikkinchi
426 for (a, b) in [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)] {
427 g.add_unweighted_edge(a, b);
428 }
429 let rang = is_bipartite(&g).unwrap();
430 for (a, b, _) in g.all_edges() {
431 assert_ne!(rang[a], rang[b]);
432 }
433 }
434}