rust_algorithms/sorting/quadratic.rs
1//! O(n²) oilasidagi "o'quv" algoritmlari + Shell sort.
2//!
3//! Ular sekin, lekin *g'oyasi* keyingi hamma narsaning poydevori.
4
5/// Bubble sort — qo'shni elementlarni almashtira-almashtira "pufakchani" yuqoriga chiqarish.
6///
7/// **G'oya:** massiv bo'ylab yurib, `arr[j] > arr[j+1]` bo'lsa almashtiramiz.
8/// Bitta to'liq yurishdan keyin eng katta element **oxirida** bo'ladi.
9///
10/// Bu yerda optimallashtirilgan variant: agar bitta yurishda hech narsa almashmasa,
11/// massiv allaqachon tartiblangan — to'xtaymiz (shuning uchun eng yaxshi holat O(n)).
12///
13/// - **Time:** O(n) / O(n²) / O(n²), **Space:** O(1), **barqaror**.
14///
15/// # Misol
16/// ```
17/// use rust_algorithms::sorting::bubble_sort;
18///
19/// let mut v = vec![5, 1, 4, 2, 8];
20/// bubble_sort(&mut v);
21/// assert_eq!(v, [1, 2, 4, 5, 8]);
22/// ```
23pub fn bubble_sort<T: Ord>(arr: &mut [T]) {
24 let n = arr.len();
25 for i in 0..n {
26 let mut swapped = false;
27 // Har yurishdan keyin oxirgi `i` ta element o'z joyida — ularga tegmaymiz.
28 for j in 0..n.saturating_sub(i + 1) {
29 if arr[j] > arr[j + 1] {
30 arr.swap(j, j + 1);
31 swapped = true;
32 }
33 }
34 if !swapped {
35 break; // allaqachon tartiblangan
36 }
37 }
38}
39
40/// Selection sort — qolgan qismdan eng kichigini topib, oldinga qo'yish.
41///
42/// **G'oya:** `i` -pozitsiya uchun `i..n` oralig'idagi minimumni topamiz va almashtiramiz.
43///
44/// Almashtirishlar soni har doim **eng ko'pi bilan n ta** — yozish (write) qimmat
45/// bo'lgan xotirada (masalan, flash) shuning uchun foydali.
46///
47/// - **Time:** O(n²) har doim, **Space:** O(1), **barqaror emas**.
48///
49/// # Misol
50/// ```
51/// use rust_algorithms::sorting::selection_sort;
52///
53/// let mut v = vec![64, 25, 12, 22, 11];
54/// selection_sort(&mut v);
55/// assert_eq!(v, [11, 12, 22, 25, 64]);
56/// ```
57pub fn selection_sort<T: Ord>(arr: &mut [T]) {
58 let n = arr.len();
59 for i in 0..n {
60 let mut min_idx = i;
61 for j in (i + 1)..n {
62 if arr[j] < arr[min_idx] {
63 min_idx = j;
64 }
65 }
66 if min_idx != i {
67 arr.swap(i, min_idx);
68 }
69 }
70}
71
72/// Insertion sort — qo'lingizdagi kartalarni terganingizdek.
73///
74/// **G'oya:** chap tomon har doim tartiblangan. Navbatdagi elementni olib,
75/// chapdagi tartiblangan qismning to'g'ri joyiga "suqib" qo'yamiz.
76///
77/// **Amaliy ahamiyati katta:** kichik massivlarda (n < 32) va **deyarli tartiblangan**
78/// ma'lumotlarda hamma narsadan tez. Shuning uchun TimSort/pdqsort ichida ishlatiladi.
79///
80/// - **Time:** O(n) (tartiblangan bo'lsa) / O(n²), **Space:** O(1), **barqaror**.
81///
82/// # Misol
83/// ```
84/// use rust_algorithms::sorting::insertion_sort;
85///
86/// let mut v = vec![12, 11, 13, 5, 6];
87/// insertion_sort(&mut v);
88/// assert_eq!(v, [5, 6, 11, 12, 13]);
89/// ```
90pub fn insertion_sort<T: Ord>(arr: &mut [T]) {
91 for i in 1..arr.len() {
92 let mut j = i;
93 // Elementni o'z joyiga yetguncha chapga surib boramiz.
94 while j > 0 && arr[j - 1] > arr[j] {
95 arr.swap(j - 1, j);
96 j -= 1;
97 }
98 }
99}
100
101/// Shell sort — insertion sortning "uzoq masofaga sakraydigan" versiyasi.
102///
103/// **G'oya:** avval bir-biridan `gap` masofada turgan elementlarni tartiblaymiz,
104/// keyin `gap` ni kichraytira boramiz. Katta gaplar elementlarni tez o'z
105/// hududiga olib keladi, oxirgi `gap = 1` esa deyarli tayyor massivni tugatadi.
106///
107/// Bu yerda Knuth ketma-ketligi ishlatilgan: 1, 4, 13, 40, 121… (`3h+1`).
108///
109/// - **Time:** ~O(n^1.3) amalda, **Space:** O(1), **barqaror emas**.
110///
111/// # Misol
112/// ```
113/// use rust_algorithms::sorting::shell_sort;
114///
115/// let mut v = vec![23, 12, 1, 8, 34, 54, 2, 3];
116/// shell_sort(&mut v);
117/// assert_eq!(v, [1, 2, 3, 8, 12, 23, 34, 54]);
118/// ```
119pub fn shell_sort<T: Ord>(arr: &mut [T]) {
120 let n = arr.len();
121 if n < 2 {
122 return;
123 }
124
125 // Knuth ketma-ketligi bo'yicha eng katta gapni topamiz
126 let mut gap = 1usize;
127 while gap < n / 3 {
128 gap = gap * 3 + 1;
129 }
130
131 while gap >= 1 {
132 for i in gap..n {
133 let mut j = i;
134 while j >= gap && arr[j - gap] > arr[j] {
135 arr.swap(j - gap, j);
136 j -= gap;
137 }
138 }
139 if gap == 1 {
140 break;
141 }
142 gap /= 3;
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149 use crate::sorting::is_sorted;
150 use crate::util::Rng;
151
152 fn holatlar() -> Vec<Vec<i64>> {
153 let mut rng = Rng::new(20_26);
154 vec![
155 vec![],
156 vec![1],
157 vec![2, 1],
158 vec![1, 2, 3, 4, 5], // tartiblangan
159 vec![5, 4, 3, 2, 1], // teskari
160 vec![3, 3, 3, 3], // hammasi teng
161 vec![-5, 0, 7, -2, 9, 9, -100], // manfiylar va takrorlar
162 rng.vec(200, -1000, 1000), // tasodifiy
163 ]
164 }
165
166 fn tekshir(sort: fn(&mut [i64])) {
167 for mut v in holatlar() {
168 let mut kutilgan = v.clone();
169 kutilgan.sort_unstable();
170 sort(&mut v);
171 assert!(is_sorted(&v), "tartiblanmadi: {v:?}");
172 assert_eq!(v, kutilgan);
173 }
174 }
175
176 #[test]
177 fn bubble() {
178 tekshir(bubble_sort);
179 }
180
181 #[test]
182 fn selection() {
183 tekshir(selection_sort);
184 }
185
186 #[test]
187 fn insertion() {
188 tekshir(insertion_sort);
189 }
190
191 #[test]
192 fn shell() {
193 tekshir(shell_sort);
194 }
195
196 #[test]
197 fn bubble_barqaror() {
198 // (kalit, tartib raqami) — kalit bo'yicha saralaganda raqamlar tartibi saqlanishi kerak
199 let mut v = vec![(1, 'a'), (0, 'b'), (1, 'c'), (0, 'd')];
200 bubble_sort(&mut v);
201 assert_eq!(v, vec![(0, 'b'), (0, 'd'), (1, 'a'), (1, 'c')]);
202 }
203}