Skip to main content

rust_algorithms/other/
backtracking.rs

1//! Backtracking — "sinab ko'r, bo'lmasa orqaga qayt".
2//!
3//! **Shablon:**
4//!
5//! ```text
6//! fn qidir(holat):
7//!     agar holat to'liq bo'lsa → javobga qo'sh, qaytish
8//!     har bir mumkin bo'lgan qadam uchun:
9//!         agar qadam qoidaga zid bo'lmasa:
10//!             qadamni QO'YAMIZ
11//!             qidir(yangi holat)      ← chuqurroq ketamiz
12//!             qadamni OLIB TASHLAYMIZ ← backtrack!
13//! ```
14//!
15//! Backtracking — bu **kesilgan** (pruned) to'liq izlash. Umid yo'q shoxni
16//! erta tashlab yuborish algoritmni amalda ishlaydigan qiladi.
17
18/// Barcha o'rin almashtirishlar (permutations).
19///
20/// `n` ta elementdan `n!` ta variant chiqadi.
21///
22/// - **Time:** O(n · n!), **Space:** O(n) (natijadan tashqari).
23///
24/// # Misol
25/// ```
26/// use rust_algorithms::other::backtracking::permutations;
27///
28/// let p = permutations(&[1, 2, 3]);
29/// assert_eq!(p.len(), 6);
30/// assert!(p.contains(&vec![2, 1, 3]));
31/// ```
32pub fn permutations<T: Clone>(items: &[T]) -> Vec<Vec<T>> {
33    fn go<T: Clone>(hozir: &mut Vec<T>, qolgan: &mut Vec<T>, out: &mut Vec<Vec<T>>) {
34        if qolgan.is_empty() {
35            out.push(hozir.clone());
36            return;
37        }
38        for i in 0..qolgan.len() {
39            let x = qolgan.remove(i); // qadamni qo'yamiz
40            hozir.push(x.clone());
41            go(hozir, qolgan, out);
42            hozir.pop(); // backtrack
43            qolgan.insert(i, x);
44        }
45    }
46    let mut out = Vec::new();
47    go(&mut Vec::new(), &mut items.to_vec(), &mut out);
48    out
49}
50
51/// Barcha qism to'plamlar (power set) — `2ⁿ` ta.
52///
53/// **G'oya:** har bir element uchun ikkita qaror — "olaman" yoki "olmayman".
54///
55/// # Misol
56/// ```
57/// use rust_algorithms::other::backtracking::subsets;
58///
59/// let s = subsets(&[1, 2, 3]);
60/// assert_eq!(s.len(), 8);
61/// assert!(s.contains(&vec![]));
62/// assert!(s.contains(&vec![1, 3]));
63/// ```
64pub fn subsets<T: Clone>(items: &[T]) -> Vec<Vec<T>> {
65    fn go<T: Clone>(items: &[T], i: usize, hozir: &mut Vec<T>, out: &mut Vec<Vec<T>>) {
66        if i == items.len() {
67            out.push(hozir.clone());
68            return;
69        }
70        go(items, i + 1, hozir, out); // olmaymiz
71        hozir.push(items[i].clone()); // olamiz
72        go(items, i + 1, hozir, out);
73        hozir.pop(); // backtrack
74    }
75    let mut out = Vec::new();
76    go(items, 0, &mut Vec::new(), &mut out);
77    out
78}
79
80/// **N vazir masalasi**: `n × n` shaxmat taxtasiga `n` ta vazirni bir-birini
81/// urmaydigan qilib joylashning barcha usullari.
82///
83/// Vazir gorizontal, vertikal va diagonal bo'ylab yuradi.
84///
85/// **Optimallashtirish:** har bir qatorga aynan bitta vazir qo'yiladi, shuning uchun
86/// faqat ustunlar va ikki diagonalni `bool` massivlarda kuzatib borish yetarli —
87/// tekshiruv **O(1)**.
88///
89/// ```text
90///  n = 4 uchun 2 ta yechim:
91///   . Q . .        . . Q .
92///   . . . Q        Q . . .
93///   Q . . .        . . . Q
94///   . . Q .        . Q . .
95/// ```
96///
97/// Qaytadi: har bir yechim — `qator → ustun` massivi.
98///
99/// # Misol
100/// ```
101/// use rust_algorithms::other::backtracking::n_queens;
102///
103/// assert_eq!(n_queens(4).len(), 2);
104/// assert_eq!(n_queens(8).len(), 92);
105/// assert_eq!(n_queens(1).len(), 1);
106/// assert_eq!(n_queens(3).len(), 0); // yechim yo'q
107/// ```
108pub fn n_queens(n: usize) -> Vec<Vec<usize>> {
109    struct Holat {
110        ustun: Vec<bool>,
111        diag1: Vec<bool>, // qator + ustun
112        diag2: Vec<bool>, // qator - ustun + n - 1
113        joylashuv: Vec<usize>,
114        yechimlar: Vec<Vec<usize>>,
115    }
116
117    fn go(h: &mut Holat, qator: usize, n: usize) {
118        if qator == n {
119            h.yechimlar.push(h.joylashuv.clone());
120            return;
121        }
122        for ustun in 0..n {
123            let d1 = qator + ustun;
124            let d2 = qator + n - 1 - ustun;
125            if h.ustun[ustun] || h.diag1[d1] || h.diag2[d2] {
126                continue; // urilib qoladi — bu shoxni kesamiz
127            }
128            h.ustun[ustun] = true;
129            h.diag1[d1] = true;
130            h.diag2[d2] = true;
131            h.joylashuv.push(ustun);
132
133            go(h, qator + 1, n);
134
135            h.joylashuv.pop(); // backtrack
136            h.ustun[ustun] = false;
137            h.diag1[d1] = false;
138            h.diag2[d2] = false;
139        }
140    }
141
142    if n == 0 {
143        return Vec::new();
144    }
145    let mut h = Holat {
146        ustun: vec![false; n],
147        diag1: vec![false; 2 * n],
148        diag2: vec![false; 2 * n],
149        joylashuv: Vec::with_capacity(n),
150        yechimlar: Vec::new(),
151    };
152    go(&mut h, 0, n);
153    h.yechimlar
154}
155
156/// Berilgan summani beradigan barcha qism to'plamlar (subset sum).
157///
158/// # Misol
159/// ```
160/// use rust_algorithms::other::backtracking::subset_sum;
161///
162/// let mut natija = subset_sum(&[2, 3, 5, 7], 10);
163/// natija.sort();
164/// assert_eq!(natija, vec![vec![2, 3, 5], vec![3, 7]]);
165/// ```
166pub fn subset_sum(nums: &[i64], target: i64) -> Vec<Vec<i64>> {
167    fn go(nums: &[i64], i: usize, qolgan: i64, hozir: &mut Vec<i64>, out: &mut Vec<Vec<i64>>) {
168        if qolgan == 0 {
169            out.push(hozir.clone());
170            return;
171        }
172        if i == nums.len() || qolgan < 0 {
173            return; // kesish (pruning)
174        }
175        hozir.push(nums[i]);
176        go(nums, i + 1, qolgan - nums[i], hozir, out);
177        hozir.pop();
178        go(nums, i + 1, qolgan, hozir, out);
179    }
180    let mut out = Vec::new();
181    go(nums, 0, target, &mut Vec::new(), &mut out);
182    out
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188
189    #[test]
190    fn permutatsiyalar_soni_va_ozgachaligi() {
191        for n in 0..=6usize {
192            let items: Vec<usize> = (0..n).collect();
193            let p = permutations(&items);
194            let kutilgan: usize = (1..=n).product::<usize>().max(1);
195            assert_eq!(p.len(), kutilgan, "n = {n}");
196
197            let mut nusxa = p.clone();
198            nusxa.sort();
199            nusxa.dedup();
200            assert_eq!(nusxa.len(), p.len(), "takrorlanish bor");
201        }
202    }
203
204    #[test]
205    fn qism_toplamlar() {
206        for n in 0..=8usize {
207            let items: Vec<usize> = (0..n).collect();
208            assert_eq!(subsets(&items).len(), 1 << n);
209        }
210    }
211
212    #[test]
213    fn n_vazir_yechimlari_haqiqiy() {
214        for n in 1..=8usize {
215            for yechim in n_queens(n) {
216                assert_eq!(yechim.len(), n);
217                for (q1, &u1) in yechim.iter().enumerate() {
218                    for (q2, &u2) in yechim.iter().enumerate().skip(q1 + 1) {
219                        assert_ne!(u1, u2, "bir ustunda");
220                        let dq = (q2 - q1) as i64;
221                        let du = (u2 as i64 - u1 as i64).abs();
222                        assert_ne!(dq, du, "diagonalda");
223                    }
224                }
225            }
226        }
227    }
228
229    #[test]
230    fn n_vazir_mashhur_sonlar() {
231        let kutilgan = [1, 0, 0, 2, 10, 4, 40, 92]; // n = 1..=8
232        for (i, &k) in kutilgan.iter().enumerate() {
233            assert_eq!(n_queens(i + 1).len(), k, "n = {}", i + 1);
234        }
235    }
236
237    #[test]
238    fn qism_toplam_summasi() {
239        assert!(subset_sum(&[1, 2, 3], 100).is_empty());
240        assert_eq!(subset_sum(&[5], 5), vec![vec![5]]);
241        let n = subset_sum(&[1, 1, 1], 2);
242        assert_eq!(n.len(), 3); // 1+1 ni uch xil tanlash mumkin
243    }
244}