rust_algorithms/graph/mst.rs
1//! Minimal bog'lovchi daraxt (Minimum Spanning Tree): Kruskal va Prim.
2//!
3//! **Masala:** hamma tugunni bir-biriga bog'lash uchun eng arzon qirralar to'plamini
4//! tanlang. Natija — daraxt (V−1 ta qirra, siklsiz).
5//!
6//! **Real hayotda:** shaharlarni optik kabel bilan bog'lash, elektr tarmog'i
7//! rejalashtirish, klasterlash (bir necha eng qimmat qirrani olib tashlab).
8
9use super::Graph;
10use crate::data_structures::{DisjointSet, MinHeap};
11use std::cmp::Reverse;
12use std::collections::BinaryHeap;
13
14/// MST natijasi: `(umumiy og'irlik, tanlangan qirralar)`.
15pub type MstResult = Option<(i64, Vec<(usize, usize, i64)>)>;
16
17/// **Kruskal** — qirralarni arzonidan boshlab tanlash (ochko'z algoritm).
18///
19/// **G'oya:**
20/// 1. Barcha qirralarni og'irligi bo'yicha tartiblaymiz;
21/// 2. Har birini navbatma-navbat olamiz: agar u **sikl hosil qilmasa** — MST ga qo'shamiz.
22/// 3. "Sikl hosil qiladimi?" savoliga [`DisjointSet`] deyarli O(1) da javob beradi.
23///
24/// - **Time:** O(E log E) — asosiy vaqt tartiblashga ketadi. **Space:** O(V).
25/// - Siyrak graflarda Primdan qulayroq.
26///
27/// Qaytadi: `(umumiy_ogirlik, qirralar)`. Graf bog'liq bo'lmasa — `None`
28/// (bu holda natija "o'rmon" bo'ladi, daraxt emas).
29///
30/// # Misol
31/// ```
32/// use rust_algorithms::graph::{Graph, kruskal};
33///
34/// let mut g = Graph::new_undirected(4);
35/// g.add_edge(0, 1, 10);
36/// g.add_edge(0, 2, 6);
37/// g.add_edge(0, 3, 5);
38/// g.add_edge(1, 3, 15);
39/// g.add_edge(2, 3, 4);
40///
41/// let (ogirlik, qirralar) = kruskal(&g).unwrap();
42/// assert_eq!(ogirlik, 19); // 4 + 5 + 10
43/// assert_eq!(qirralar.len(), 3); // V − 1
44/// ```
45pub fn kruskal(g: &Graph) -> MstResult {
46 let n = g.node_count();
47 if n == 0 {
48 return Some((0, Vec::new()));
49 }
50
51 let mut qirralar = g.all_edges();
52 qirralar.sort_by_key(|&(_, _, w)| w); // arzonidan qimmatiga
53
54 let mut ds = DisjointSet::new(n);
55 let mut mst = Vec::with_capacity(n - 1);
56 let mut jami = 0i64;
57
58 for (u, v, w) in qirralar {
59 // union() false qaytarsa — u va v allaqachon bog'langan, bu qirra sikl yasaydi
60 if ds.union(u, v) {
61 mst.push((u, v, w));
62 jami += w;
63 if mst.len() == n - 1 {
64 break; // daraxt to'ldi
65 }
66 }
67 }
68
69 if mst.len() == n - 1 {
70 Some((jami, mst))
71 } else {
72 None // graf bog'liq emas
73 }
74}
75
76/// **Prim** — daraxtni bitta tugundan boshlab "o'stirish".
77///
78/// **G'oya:** MST ni bitta tugundan boshlaymiz va har qadamda daraxtni tashqi
79/// dunyoga bog'laydigan **eng arzon** qirrani qo'shamiz. Nomzod qirralarni
80/// min-heapda saqlaymiz.
81///
82/// - **Time:** O(E log V), **Space:** O(V + E).
83/// - Zich graflarda (E ≈ V²) Kruskaldan tezroq.
84///
85/// # Misol
86/// ```
87/// use rust_algorithms::graph::{Graph, kruskal, prim};
88///
89/// let mut g = Graph::new_undirected(5);
90/// for (a, b, w) in [(0, 1, 2), (0, 3, 6), (1, 2, 3), (1, 3, 8), (1, 4, 5), (2, 4, 7), (3, 4, 9)] {
91/// g.add_edge(a, b, w);
92/// }
93///
94/// let (prim_w, _) = prim(&g).unwrap();
95/// let (kruskal_w, _) = kruskal(&g).unwrap();
96/// assert_eq!(prim_w, kruskal_w); // ikkalasi ham 16
97/// ```
98pub fn prim(g: &Graph) -> MstResult {
99 let n = g.node_count();
100 if n == 0 {
101 return Some((0, Vec::new()));
102 }
103
104 let mut daraxtda = vec![false; n];
105 let mut mst = Vec::with_capacity(n - 1);
106 let mut jami = 0i64;
107
108 // (og'irlik, qayerdan, qayerga) — Reverse bilan min-heap
109 let mut heap = BinaryHeap::new();
110 daraxtda[0] = true;
111 for e in g.neighbors(0) {
112 heap.push(Reverse((e.weight, 0usize, e.to)));
113 }
114
115 while let Some(Reverse((w, from, to))) = heap.pop() {
116 if daraxtda[to] {
117 continue; // bu tugun allaqachon daraxtda — qirra sikl yasaydi
118 }
119 daraxtda[to] = true;
120 mst.push((from, to, w));
121 jami += w;
122
123 for e in g.neighbors(to) {
124 if !daraxtda[e.to] {
125 heap.push(Reverse((e.weight, to, e.to)));
126 }
127 }
128 }
129
130 if mst.len() == n - 1 {
131 Some((jami, mst))
132 } else {
133 None
134 }
135}
136
137/// Ichki tekshiruv: `MinHeap` ham shu maqsadda ishlatilishi mumkinligini ko'rsatadi.
138#[allow(dead_code)]
139fn min_heap_bilan_misol(ogirliklar: Vec<i64>) -> Vec<i64> {
140 let heap: MinHeap<i64> = ogirliklar.into_iter().collect();
141 heap.into_sorted_vec()
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147 use crate::util::Rng;
148
149 fn namuna() -> Graph {
150 let mut g = Graph::new_undirected(7);
151 for (a, b, w) in [
152 (0, 1, 7),
153 (0, 3, 5),
154 (1, 2, 8),
155 (1, 3, 9),
156 (1, 4, 7),
157 (2, 4, 5),
158 (3, 4, 15),
159 (3, 5, 6),
160 (4, 5, 8),
161 (4, 6, 9),
162 (5, 6, 11),
163 ] {
164 g.add_edge(a, b, w);
165 }
166 g
167 }
168
169 #[test]
170 fn kruskal_klassik_misol() {
171 let (w, qirralar) = kruskal(&namuna()).unwrap();
172 assert_eq!(w, 39); // ma'lum javob
173 assert_eq!(qirralar.len(), 6);
174 }
175
176 #[test]
177 fn prim_va_kruskal_bir_xil_ogirlik() {
178 let mut rng = Rng::new(606);
179 for _ in 0..30 {
180 let n = 8;
181 let mut g = Graph::new_undirected(n);
182 // bog'liqlikni kafolatlash uchun avval zanjir quramiz
183 for i in 1..n {
184 g.add_edge(i - 1, i, rng.range(1, 50));
185 }
186 for _ in 0..12 {
187 let (a, b) = (rng.below(n), rng.below(n));
188 if a != b {
189 g.add_edge(a, b, rng.range(1, 50));
190 }
191 }
192 let (kw, ke) = kruskal(&g).unwrap();
193 let (pw, pe) = prim(&g).unwrap();
194 assert_eq!(kw, pw);
195 assert_eq!(ke.len(), n - 1);
196 assert_eq!(pe.len(), n - 1);
197 }
198 }
199
200 #[test]
201 fn boglanmagan_grafda_none() {
202 let mut g = Graph::new_undirected(4);
203 g.add_edge(0, 1, 1);
204 g.add_edge(2, 3, 1);
205 assert!(kruskal(&g).is_none());
206 assert!(prim(&g).is_none());
207 }
208
209 #[test]
210 fn bitta_tugun() {
211 let g = Graph::new_undirected(1);
212 assert_eq!(kruskal(&g).unwrap(), (0, vec![]));
213 assert_eq!(prim(&g).unwrap(), (0, vec![]));
214 }
215
216 #[test]
217 fn mstda_sikl_yoq() {
218 let (_, qirralar) = kruskal(&namuna()).unwrap();
219 let mut ds = DisjointSet::new(7);
220 for (u, v, _) in qirralar {
221 assert!(ds.union(u, v), "MST da sikl bor!");
222 }
223 assert_eq!(ds.count(), 1);
224 }
225}