Skip to main content

rust_algorithms/other/
two_pointers.rs

1//! Ikki ko'rsatkich (two pointers) texnikasi.
2//!
3//! **G'oya:** ichma-ich ikki tsikl (O(n²)) o'rniga, ikkita indeksni **bir yo'nalishda
4//! yoki qarama-qarshi** harakatlantirib, bitta yurishda javob topamiz — O(n).
5//!
6//! Ko'pincha massiv **tartiblangan** bo'lishi kerak.
7
8/// Tartiblangan massivda yig'indisi `target` ga teng ikkita son topadi.
9///
10/// **G'oya:** ikki ko'rsatkich — biri boshda, biri oxirida.
11/// Yig'indi katta bo'lsa o'ngdagini chapga suramiz, kichik bo'lsa chapdagini o'ngga.
12///
13/// ```text
14///  [1, 3, 4, 6, 8, 11]   target = 10
15///   ↑              ↑     1 + 11 = 12 > 10 → o'ngni suramiz
16///   ↑           ↑        1 + 8  = 9  < 10 → chapni suramiz
17///      ↑        ↑        3 + 8  = 11 > 10 → o'ngni suramiz
18///      ↑     ↑           3 + 6  = 9  < 10 → chapni suramiz
19///         ↑  ↑           4 + 6  = 10 ✓
20/// ```
21///
22/// - **Time:** O(n) (hash-jadvalsiz!), **Space:** O(1).
23///
24/// # Misol
25/// ```
26/// use rust_algorithms::other::two_pointers::two_sum_sorted;
27///
28/// assert_eq!(two_sum_sorted(&[1, 3, 4, 6, 8, 11], 10), Some((2, 3)));
29/// assert_eq!(two_sum_sorted(&[1, 2], 100), None);
30/// ```
31pub fn two_sum_sorted(nums: &[i64], target: i64) -> Option<(usize, usize)> {
32    if nums.len() < 2 {
33        return None;
34    }
35    let (mut chap, mut ong) = (0usize, nums.len() - 1);
36    while chap < ong {
37        let yigindi = nums[chap] + nums[ong];
38        match yigindi.cmp(&target) {
39            std::cmp::Ordering::Equal => return Some((chap, ong)),
40            std::cmp::Ordering::Less => chap += 1,
41            std::cmp::Ordering::Greater => ong -= 1,
42        }
43    }
44    None
45}
46
47/// Tartiblangan massivdan takrorlarni **joyida** olib tashlaydi.
48///
49/// Qaytadi: yangi (noyob) elementlar soni. Massivning shu qadar boshlang'ich qismi
50/// to'g'ri qiymatlarni saqlaydi.
51///
52/// **G'oya:** `yozish` ko'rsatkichi noyob qiymatlar uchun, `oqish` — skanerlash uchun.
53///
54/// - **Time:** O(n), **Space:** O(1).
55///
56/// # Misol
57/// ```
58/// use rust_algorithms::other::two_pointers::remove_duplicates;
59///
60/// let mut v = vec![1, 1, 2, 2, 2, 3, 4, 4];
61/// let n = remove_duplicates(&mut v);
62/// assert_eq!(n, 4);
63/// assert_eq!(&v[..n], &[1, 2, 3, 4]);
64/// ```
65pub fn remove_duplicates<T: PartialEq + Copy>(nums: &mut [T]) -> usize {
66    if nums.is_empty() {
67        return 0;
68    }
69    let mut yozish = 1usize;
70    for oqish in 1..nums.len() {
71        if nums[oqish] != nums[yozish - 1] {
72            nums[yozish] = nums[oqish];
73            yozish += 1;
74        }
75    }
76    yozish
77}
78
79/// Eng ko'p suv sig'diradigan idish (container with most water).
80///
81/// Balandliklari berilgan ustunlardan ikkitasini tanlab, ular orasidagi
82/// maksimal suv hajmini topamiz: `hajm = min(h[i], h[j]) × (j − i)`.
83///
84/// **G'oya:** ikki chetdan boshlaymiz va **pastroq** devorni ichkariga suramiz —
85/// chunki uni qoldirib, kenglikni kamaytirish hech qachon foyda bermaydi.
86///
87/// - **Time:** O(n), **Space:** O(1).
88///
89/// # Misol
90/// ```
91/// use rust_algorithms::other::two_pointers::max_water_container;
92///
93/// assert_eq!(max_water_container(&[1, 8, 6, 2, 5, 4, 8, 3, 7]), 49);
94/// assert_eq!(max_water_container(&[1, 1]), 1);
95/// ```
96pub fn max_water_container(heights: &[i64]) -> i64 {
97    if heights.len() < 2 {
98        return 0;
99    }
100    let (mut chap, mut ong) = (0usize, heights.len() - 1);
101    let mut eng_katta = 0i64;
102
103    while chap < ong {
104        let hajm = heights[chap].min(heights[ong]) * (ong - chap) as i64;
105        eng_katta = eng_katta.max(hajm);
106        if heights[chap] < heights[ong] {
107            chap += 1;
108        } else {
109            ong -= 1;
110        }
111    }
112    eng_katta
113}
114
115/// Yig'indisi nolga teng bo'lgan barcha uchliklar (3Sum) — takrorsiz.
116///
117/// **G'oya:** massivni tartiblab, har bir element uchun qolganida
118/// [`two_sum_sorted`] mantiqini qo'llaymiz.
119///
120/// - **Time:** O(n²), **Space:** O(n) (natijadan tashqari).
121///
122/// # Misol
123/// ```
124/// use rust_algorithms::other::two_pointers::three_sum_zero;
125///
126/// let mut r = three_sum_zero(&[-1, 0, 1, 2, -1, -4]);
127/// r.sort();
128/// assert_eq!(r, vec![vec![-1, -1, 2], vec![-1, 0, 1]]);
129/// ```
130pub fn three_sum_zero(nums: &[i64]) -> Vec<Vec<i64>> {
131    let mut v = nums.to_vec();
132    v.sort_unstable();
133    let n = v.len();
134    let mut natija = Vec::new();
135
136    for i in 0..n.saturating_sub(2) {
137        if i > 0 && v[i] == v[i - 1] {
138            continue; // takror uchliklardan qochamiz
139        }
140        let (mut chap, mut ong) = (i + 1, n - 1);
141        while chap < ong {
142            let yigindi = v[i] + v[chap] + v[ong];
143            match yigindi.cmp(&0) {
144                std::cmp::Ordering::Equal => {
145                    natija.push(vec![v[i], v[chap], v[ong]]);
146                    while chap < ong && v[chap] == v[chap + 1] {
147                        chap += 1;
148                    }
149                    while chap < ong && v[ong] == v[ong - 1] {
150                        ong -= 1;
151                    }
152                    chap += 1;
153                    ong -= 1;
154                }
155                std::cmp::Ordering::Less => chap += 1,
156                std::cmp::Ordering::Greater => ong -= 1,
157            }
158        }
159    }
160    natija
161}
162
163#[cfg(test)]
164mod tests {
165    use super::*;
166    use crate::util::Rng;
167
168    #[test]
169    fn two_sum_bruteforce_bilan_mos() {
170        let mut rng = Rng::new(1001);
171        for _ in 0..50 {
172            let mut v = rng.vec(30, -30, 30);
173            v.sort_unstable();
174            let target = rng.range(-40, 40);
175
176            let natija = two_sum_sorted(&v, target);
177            let kutilgan_bor =
178                (0..v.len()).any(|i| ((i + 1)..v.len()).any(|j| v[i] + v[j] == target));
179
180            assert_eq!(natija.is_some(), kutilgan_bor, "target = {target}");
181            if let Some((i, j)) = natija {
182                assert_eq!(v[i] + v[j], target);
183            }
184        }
185    }
186
187    #[test]
188    fn takrorlarni_olib_tashlash() {
189        let mut bosh: Vec<i32> = vec![];
190        assert_eq!(remove_duplicates(&mut bosh), 0);
191
192        let mut v = vec![7];
193        assert_eq!(remove_duplicates(&mut v), 1);
194
195        let mut hammasi_teng = vec![3, 3, 3, 3];
196        assert_eq!(remove_duplicates(&mut hammasi_teng), 1);
197
198        let mut noyob = vec![1, 2, 3];
199        assert_eq!(remove_duplicates(&mut noyob), 3);
200    }
201
202    #[test]
203    fn idish_hajmi() {
204        assert_eq!(max_water_container(&[]), 0);
205        assert_eq!(max_water_container(&[5]), 0);
206        assert_eq!(max_water_container(&[4, 3, 2, 1, 4]), 16);
207        assert_eq!(max_water_container(&[1, 2, 1]), 2);
208    }
209
210    #[test]
211    fn uchliklar_togri_va_takrorsiz() {
212        let mut rng = Rng::new(1002);
213        for _ in 0..20 {
214            let v = rng.vec(25, -10, 10);
215            let uchliklar = three_sum_zero(&v);
216            for u in &uchliklar {
217                assert_eq!(u.iter().sum::<i64>(), 0);
218            }
219            let mut nusxa = uchliklar.clone();
220            nusxa.sort();
221            nusxa.dedup();
222            assert_eq!(nusxa.len(), uchliklar.len());
223        }
224        assert!(three_sum_zero(&[1, 2, 3]).is_empty());
225    }
226}