Skip to main content

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}