dsa/algorithms/
sorting.rs

1use crate::data_structures::heap::Heap;
2
3/// Sorts a slice using the Bubble Sort algorithm.
4///
5/// The bubble sort algorithm repeatedly steps through the list, compares adjacent
6/// elements, and swaps them if they are in the wrong order. This process continues
7/// until the list is sorted.
8///
9/// # Parameters
10/// - `arr`: A mutable reference to the slice of integers to be sorted.
11///
12/// # Time Complexity
13/// - Best: `O(n)`
14/// - Worst: `O(n²)`
15/// - Average: `O(n²)`
16///
17/// # Space Complexity
18/// - `O(1)` (in-place sorting)
19///
20/// # Examples
21/// ```
22/// use dsa::algorithms::sorting::bubble_sort;
23///
24/// let mut arr = [5, 3, 8, 4, 2];
25/// bubble_sort(&mut arr);
26/// assert_eq!(arr, [2, 3, 4, 5, 8]);
27/// ```
28pub fn bubble_sort(arr: &mut [i32]) {
29    for i in 0..arr.len() {
30        for j in 1..(arr.len() - i) {
31            if arr[j] < arr[j - 1] {
32                arr.swap(j, j - 1);
33            }
34        }
35    }
36}
37
38/// Sorts a slice using the Insertion Sort algorithm.
39///
40/// Insertion sort builds the sorted array one element at a time. It takes each new element
41/// and inserts it into its correct position within the sorted portion of the array.
42///
43/// ### Parameters
44/// - `arr`: A mutable reference to the slice of integers to be sorted.
45///
46/// ### Time Complexity
47/// - Best: `O(n)`
48/// - Worst: `O(n²)`
49/// - Average: `O(n²)`
50///
51/// ### Space Complexity
52/// - `O(1)` (in-place sorting)
53/// 
54/// ### Examples
55/// ```
56/// use dsa::algorithms::sorting::insertion_sort;
57///
58/// let mut arr = [5, 3, 8, 4, 2];
59/// insertion_sort(&mut arr);
60/// assert_eq!(arr, [2, 3, 4, 5, 8]);
61/// ```
62pub fn insertion_sort(arr: &mut [i32]) {
63    for i in 1..arr.len() {
64        let mut j = i;
65        while j > 0 && arr[j - 1] > arr[j] {
66            arr.swap(j, j - 1);
67            j -= 1;
68        }
69    }
70}
71
72/// Sorts a slice using the Selection Sort algorithm.
73///
74/// Selection sort repeatedly selects the minimum element from the unsorted portion of the array
75/// and swaps it with the first unsorted element.
76///
77/// # Parameters
78/// - `arr`: A mutable reference to the slice of integers to be sorted.
79///
80/// # Time Complexity
81/// - Best: `O(n²)`
82/// - Worst: `O(n²)`
83/// - Average: `O(n²)`
84///
85/// # Space Complexity
86/// - `O(1)` (in-place sorting)
87///
88/// # Examples
89/// ```
90/// use dsa::algorithms::sorting::selection_sort;
91///
92/// let mut arr = [5, 3, 8, 4, 2];
93/// selection_sort(&mut arr);
94/// assert_eq!(arr, [2, 3, 4, 5, 8]);
95/// ```
96pub fn selection_sort(arr: &mut [i32]) {
97    for i in 0..arr.len() {
98        let mut min_index = i;
99        for j in i + 1..arr.len() {
100            if arr[j] < arr[min_index] {
101                min_index = j;
102            }
103        }
104        arr.swap(i, min_index);
105    }
106}
107
108/// Sorts a slice using the Merge Sort algorithm.
109///
110/// Merge sort is a divide-and-conquer algorithm that splits the array into two halves, 
111/// recursively sorts them, and then merges the sorted halves.
112///
113/// # Parameters
114/// - `arr`: A mutable reference to the slice of integers to be sorted.
115///
116/// # Time Complexity
117/// - Best: `O(n log n)`
118/// - Worst: `O(n log n)`
119/// - Average: `O(n log n)`
120///
121/// # Space Complexity
122/// - `O(n)` (requires additional space for merging)
123/// 
124/// # Examples
125/// ```
126/// use dsa::algorithms::sorting::merge_sort;
127///
128/// let mut arr = [5, 3, 8, 4, 2];
129/// merge_sort(&mut arr);
130/// assert_eq!(arr, [2, 3, 4, 5, 8]);
131/// ```
132pub fn merge_sort(arr: &mut [i32]) {
133    if arr.len() <= 1 {
134        return;
135    }
136    let mid = arr.len() / 2;
137    merge_sort(&mut arr[..mid]);
138    merge_sort(&mut arr[mid..]);
139    merge(arr, mid);
140}
141/// Merges two sorted halves of a slice.
142///
143/// # Parameters
144/// - `arr`: The slice to merge the halves into.
145/// - `mid`: The index where the array is split into two halves.
146fn merge(arr: &mut [i32], mid: usize) {
147    let left = arr[..mid].to_vec();
148    let right = arr[mid..].to_vec();
149
150    let (mut i, mut j, mut k) = (0, 0, 0);
151
152    while i < left.len() && j < right.len() {
153        if left[i] <= right[j] {
154            arr[k] = left[i];
155            i += 1;
156        } else {
157            arr[k] = right[j];
158            j += 1;
159        }
160        k += 1;
161    }
162
163    while i < left.len() {
164        arr[k] = left[i];
165        i += 1;
166        k += 1;
167    }
168
169    while j < right.len() {
170        arr[k] = right[j];
171        j += 1;
172        k += 1;
173    }
174}
175
176/// Sorts a slice using the Quick Sort algorithm.
177///
178/// Quick Sort is a divide-and-conquer algorithm that picks a 'pivot' element from the array,
179/// partitions the array around the pivot, and recursively sorts the two subarrays.
180///
181/// # Parameters
182/// - `arr`: A mutable reference to the slice of integers to be sorted.
183///
184/// # Time Complexity
185/// - Best: `O(n log n)`
186/// - Worst: `O(n²)`
187/// - Average: `O(n log n)`
188///
189/// # Space Complexity
190/// - `O(log n)` (due to recursion stack)
191///
192/// # Examples
193/// ```
194/// use dsa::algorithms::sorting::quick_sort;
195///
196/// let mut arr = [5, 3, 8, 4, 2];
197/// quick_sort(&mut arr);
198/// assert_eq!(arr, [2, 3, 4, 5, 8]);
199/// ```
200pub fn quick_sort(arr: &mut [i32]) {
201    if arr.len() <= 1 {
202        return;
203    }
204
205    let pivot_index = partition(arr);
206
207    let (left, right) = arr.split_at_mut(pivot_index);
208    quick_sort(left);
209    quick_sort(&mut right[1..]);
210}
211/// Partitions the slice around a pivot element.
212///
213/// # Parameters
214/// - `arr`: A mutable reference to the slice to be partitioned.
215///
216/// # Returns
217/// - The index of the pivot element after partitioning.
218fn partition(arr: &mut [i32]) -> usize {
219    let pivot_index = arr.len() - 1;
220    let pivot = arr[pivot_index];
221    let mut i = 0;
222
223    for j in 0..arr.len() - 1 {
224        if arr[j] < pivot {
225            arr.swap(i, j);
226            i += 1;
227        }
228    }
229
230    arr.swap(i, pivot_index);
231    i
232}
233
234/// Sorts a slice using the Counting Sort algorithm.
235///
236/// Counting sort works by counting the occurrences of each element and then reconstructing the sorted
237/// array based on the counts.
238///
239/// # Parameters
240/// - `arr`: A mutable reference to the slice of integers to be sorted.
241///
242/// # Time Complexity
243/// - Best: `O(n + k)`
244/// - Worst: `O(n + k)`
245/// - Average: `O(n + k)`
246///
247/// # Space Complexity
248/// - `O(k)` (requires extra space for the count array)
249///
250/// # Examples
251/// ```
252/// use dsa::algorithms::sorting::counting_sort;
253///
254/// let mut arr = [5, 3, 8, 4, 2];
255/// counting_sort(&mut arr);
256/// assert_eq!(arr, [2, 3, 4, 5, 8]);
257/// ```
258pub fn counting_sort(arr: &mut [i32]) {
259    let max = *arr.iter().max().unwrap();
260    let mut count_arr = vec![0; max as usize + 1];
261    
262    for &num in arr.iter() {
263        count_arr[num as usize] += 1;
264    }
265    
266    let mut index = 0;
267    for (num, &count) in count_arr.iter().enumerate() {
268        for _ in 0..count {
269            arr[index] = num as i32;
270            index += 1;
271        }
272    }
273}
274
275/// Sorts a slice using the Radix Sort algorithm.
276///
277/// Radix sort processes the elements digit by digit, sorting them by each digit starting from the least significant digit.
278///
279/// # Parameters
280/// - `arr`: A mutable reference to the slice of integers to be sorted.
281///
282/// # Time Complexity
283/// - Best: `O(nk)`
284/// - Worst: `O(nk)`
285/// - Average: `O(nk)`
286///
287/// # Space Complexity
288/// - `O(n + k)` (requires additional space for the buckets)
289///
290/// # Examples
291/// ```
292/// use dsa::algorithms::sorting::radix_sort;
293///
294/// let mut arr = [170, 45, 75, 90, 802, 24, 2, 66];
295/// radix_sort(&mut arr);
296/// assert_eq!(arr, [2, 24, 45, 66, 75, 90, 170, 802]);
297/// ```
298pub fn radix_sort(arr: &mut [i32]) {
299    let max_val = *arr.iter().max().unwrap();
300    let mut exp = 1;
301
302    while max_val / exp > 0 {
303        let mut radix_array = vec![vec![]; 10];
304
305        for &num in arr.iter() {
306            let digit = (num / exp) % 10;
307            radix_array[digit as usize].push(num);
308        }
309
310        let mut idx = 0;
311        for bucket in radix_array.iter() {
312            for &num in bucket.iter() {
313                arr[idx] = num;
314                idx += 1;
315            }
316        }
317
318        exp *= 10;
319    }
320}
321
322/// Sorts a heap in ascending order (min heap) or descending order (max heap)
323/// 
324/// Heap sort transforms its elements into a heap, then repeatedly removes
325/// the largest (or smallest) element and places it in the correct position,
326/// restoring the heap property after each removal.
327/// 
328/// # Parameters
329/// - `&mut Heap<i32>`: a mutable reference to a `Heap` instance and sorts its elements
330/// 
331/// If the heap is configured as a max-heap, the elements
332/// will be sorted in ascending order. If it's a min-heap, the elements
333/// will be sorted in descending order.
334/// 
335/// # Time Complexity
336/// - Best: `O(n log n)`
337/// - Worst: `O(n log n)`
338/// - Average: `O(n log n)`
339/// 
340/// # Space Complexity
341/// - `O(n + k)`
342///
343/// # Examples
344/// ```
345/// use dsa::algorithms::sorting::heap_sort;
346/// use dsa::data_structures::heap::Heap;
347/// 
348/// let mut heap = Heap::new(false); // Min-heap
349/// heap.values = vec![3, 1, 6, 5, 2, 4];
350/// heap_sort(&mut heap);
351/// assert_eq!(heap.values, vec![6, 5, 4, 3, 2, 1]);
352/// ```
353pub fn heap_sort(heap: &mut Heap<i32>) {
354    heap.build_heap();
355    let mut considered = heap.values.len();
356    
357    while considered > 1 {
358        heap.values.swap(0, considered - 1);
359        considered -= 1;
360        heap.sift_down(0, considered);
361    }
362}