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}