rust_algorithms/searching/binary.rs
1//! Binary search (ikkilik qidiruv) va uning oilasi.
2//!
3//! Binary search — DSA dagi eng ko'p ishlatiladigan, eng ko'p xato qilinadigan algoritm.
4//! Shu sababli bu yerda uchta muhim variant bor:
5//! aniq qiymat ([`binary_search`]), chegaralar ([`lower_bound`] / [`upper_bound`])
6//! va "javob ustidan qidiruv" ([`binary_search_answer`]).
7
8use std::cmp::Ordering;
9
10/// Tartiblangan slicedan `target` ni topadi va indeksini qaytaradi.
11///
12/// **G'oya:** o'rtaga qaraymiz. Katta bo'lsa — chap yarmiga, kichik bo'lsa — o'ng yarmiga
13/// o'tamiz. Har qadamda qidiruv maydoni **2 baravar** qisqaradi.
14///
15/// - **Shart:** slice o'sish tartibida tartiblangan bo'lishi kerak.
16/// - **Time:** O(log n), **Space:** O(1).
17///
18/// # Xavfsizlik haqida eslatma
19///
20/// Klassik `mid = (left + right) / 2` yozuvi `usize` da **overflow** yoki
21/// `right = mid - 1` da **underflow** (panic) beradi. Shuning uchun bu yerda
22/// yarim ochiq oraliq `[lo, hi)` ishlatilgan — u hech qachon manfiy bo'lmaydi.
23///
24/// # Misol
25/// ```
26/// use rust_algorithms::searching::binary_search;
27///
28/// let v = [1, 3, 5, 7, 9, 11];
29/// assert_eq!(binary_search(&v, &7), Some(3));
30/// assert_eq!(binary_search(&v, &8), None);
31/// ```
32pub fn binary_search<T: Ord>(list: &[T], target: &T) -> Option<usize> {
33 let mut lo = 0usize;
34 let mut hi = list.len(); // yarim ochiq oraliq: [lo, hi)
35
36 while lo < hi {
37 let mid = lo + (hi - lo) / 2; // overflowdan himoya
38 match list[mid].cmp(target) {
39 Ordering::Equal => return Some(mid),
40 Ordering::Less => lo = mid + 1,
41 Ordering::Greater => hi = mid,
42 }
43 }
44 None
45}
46
47/// Binary search ning rekursiv ko'rinishi — o'quv maqsadida.
48///
49/// Amalda iterativ variant afzal: rekursiya stack joy egallaydi (O(log n)).
50///
51/// # Misol
52/// ```
53/// use rust_algorithms::searching::binary_search_recursive;
54///
55/// let v = [2, 4, 6, 8];
56/// assert_eq!(binary_search_recursive(&v, &2), Some(0));
57/// assert_eq!(binary_search_recursive(&v, &5), None);
58/// ```
59pub fn binary_search_recursive<T: Ord>(list: &[T], target: &T) -> Option<usize> {
60 fn go<T: Ord>(list: &[T], target: &T, lo: usize, hi: usize) -> Option<usize> {
61 if lo >= hi {
62 return None;
63 }
64 let mid = lo + (hi - lo) / 2;
65 match list[mid].cmp(target) {
66 Ordering::Equal => Some(mid),
67 Ordering::Less => go(list, target, mid + 1, hi),
68 Ordering::Greater => go(list, target, lo, mid),
69 }
70 }
71 go(list, target, 0, list.len())
72}
73
74/// **Birinchi** `>= target` elementning indeksi (C++ dagi `lower_bound`).
75///
76/// Agar bunday element bo'lmasa, `list.len()` qaytadi — ya'ni "oxiriga qo'shish kerak".
77/// Bu funksiya yordamida takrorlanuvchi qiymatlarning **eng chap** o'rnini topish mumkin.
78///
79/// - **Time:** O(log n), **Space:** O(1).
80///
81/// # Misol
82/// ```
83/// use rust_algorithms::searching::lower_bound;
84///
85/// let v = [1, 2, 2, 2, 5];
86/// assert_eq!(lower_bound(&v, &2), 1); // birinchi 2
87/// assert_eq!(lower_bound(&v, &3), 4); // 3 yo'q → 5 turgan joy
88/// assert_eq!(lower_bound(&v, &9), 5); // hammasidan katta
89/// ```
90pub fn lower_bound<T: Ord>(list: &[T], target: &T) -> usize {
91 let mut lo = 0usize;
92 let mut hi = list.len();
93 while lo < hi {
94 let mid = lo + (hi - lo) / 2;
95 if list[mid] < *target {
96 lo = mid + 1;
97 } else {
98 hi = mid;
99 }
100 }
101 lo
102}
103
104/// **Birinchi** `> target` elementning indeksi (C++ dagi `upper_bound`).
105///
106/// `upper_bound - lower_bound` = `target` necha marta uchraganini beradi (O(log n) da!).
107///
108/// # Misol
109/// ```
110/// use rust_algorithms::searching::{lower_bound, upper_bound};
111///
112/// let v = [1, 2, 2, 2, 5];
113/// assert_eq!(upper_bound(&v, &2), 4);
114/// let nechta = upper_bound(&v, &2) - lower_bound(&v, &2);
115/// assert_eq!(nechta, 3);
116/// ```
117pub fn upper_bound<T: Ord>(list: &[T], target: &T) -> usize {
118 let mut lo = 0usize;
119 let mut hi = list.len();
120 while lo < hi {
121 let mid = lo + (hi - lo) / 2;
122 if list[mid] <= *target {
123 lo = mid + 1;
124 } else {
125 hi = mid;
126 }
127 }
128 lo
129}
130
131/// **Javob ustidan binary search** — olimpiada va real hayotdagi eng kuchli usul.
132///
133/// `lo..=hi` oralig'ida `pred` monoton (`false false ... false true true ... true`)
134/// bo'lsa, `pred(x) == true` bo'lgan **eng kichik** `x` ni qaytaradi.
135/// Agar hech biri `true` bo'lmasa — `None`.
136///
137/// **Qachon kerak:** "eng kichik sig'im", "eng qisqa vaqt", "minimal tezlik" tipidagi
138/// masalalarda. Massiv emas, **javoblar oralig'i** bo'ylab qidiramiz.
139///
140/// - **Time:** O(log(hi-lo) × pred narxi), **Space:** O(1).
141///
142/// # Misol: √x ning butun qismi
143/// ```
144/// use rust_algorithms::searching::binary_search_answer;
145///
146/// let x = 1_000_000i64;
147/// // "s*s >= x" birinchi marta rost bo'ladigan s ni topamiz
148/// let s = binary_search_answer(0, x, |s| s * s >= x).unwrap();
149/// assert_eq!(s, 1000);
150/// ```
151pub fn binary_search_answer<F: Fn(i64) -> bool>(lo: i64, hi: i64, pred: F) -> Option<i64> {
152 let (mut lo, mut hi) = (lo, hi);
153 if lo > hi {
154 return None;
155 }
156 if !pred(hi) {
157 return None; // hech qayerda rost emas
158 }
159 while lo < hi {
160 let mid = lo + (hi - lo) / 2;
161 if pred(mid) {
162 hi = mid;
163 } else {
164 lo = mid + 1;
165 }
166 }
167 Some(lo)
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 #[test]
175 fn hamma_elementni_topadi() {
176 let v: Vec<i32> = (0..100).map(|x| x * 3).collect();
177 for (i, x) in v.iter().enumerate() {
178 assert_eq!(binary_search(&v, x), Some(i));
179 assert_eq!(binary_search_recursive(&v, x), Some(i));
180 }
181 }
182
183 #[test]
184 fn yoq_elementga_none() {
185 let v = [1, 3, 5, 7];
186 for x in [0, 2, 4, 6, 8] {
187 assert_eq!(binary_search(&v, &x), None, "x = {x}");
188 }
189 }
190
191 #[test]
192 fn bosh_va_bitta_elementli() {
193 let bosh: [i32; 0] = [];
194 assert_eq!(binary_search(&bosh, &1), None);
195 assert_eq!(binary_search(&[42], &42), Some(0));
196 assert_eq!(binary_search(&[42], &7), None);
197 }
198
199 #[test]
200 fn chegaralar_takrorlanuvchi_qiymatlarda() {
201 let v = [1, 2, 2, 2, 2, 9];
202 assert_eq!(lower_bound(&v, &2), 1);
203 assert_eq!(upper_bound(&v, &2), 5);
204 assert_eq!(upper_bound(&v, &0), 0);
205 assert_eq!(lower_bound(&v, &100), v.len());
206 }
207
208 #[test]
209 fn javob_ustidan_qidiruv() {
210 assert_eq!(binary_search_answer(0, 100, |x| x >= 37), Some(37));
211 assert_eq!(binary_search_answer(0, 10, |x| x > 100), None);
212 assert_eq!(binary_search_answer(0, 10_000, |s| s * s >= 144), Some(12));
213 }
214
215 #[test]
216 fn std_bilan_solishtirish() {
217 let v: Vec<i32> = (0..1000).step_by(7).collect();
218 for x in 0..1000 {
219 let ours = binary_search(&v, &x);
220 let theirs = v.binary_search(&x).ok();
221 assert_eq!(ours, theirs, "x = {x}");
222 }
223 }
224}