|
1 |
| -/// Sort a mutable slice using heap sort. |
2 |
| -/// |
3 |
| -/// Heap sort is an in-place O(n log n) sorting algorithm. It is based on a |
4 |
| -/// max heap, a binary tree data structure whose main feature is that |
5 |
| -/// parent nodes are always greater or equal to their child nodes. |
6 |
| -/// |
7 |
| -/// # Max Heap Implementation |
| 1 | +//! This module provides functions for heap sort algorithm. |
| 2 | +
|
| 3 | +use std::cmp::Ordering; |
| 4 | + |
| 5 | +/// Builds a heap from the provided array. |
8 | 6 | ///
|
9 |
| -/// A max heap can be efficiently implemented with an array. |
10 |
| -/// For example, the binary tree: |
11 |
| -/// ```text |
12 |
| -/// 1 |
13 |
| -/// 2 3 |
14 |
| -/// 4 5 6 7 |
15 |
| -/// ``` |
| 7 | +/// This function builds either a max heap or a min heap based on the `is_max_heap` parameter. |
16 | 8 | ///
|
17 |
| -/// ... is represented by the following array: |
18 |
| -/// ```text |
19 |
| -/// 1 23 4567 |
20 |
| -/// ``` |
| 9 | +/// # Arguments |
21 | 10 | ///
|
22 |
| -/// Given the index `i` of a node, parent and child indices can be calculated |
23 |
| -/// as follows: |
24 |
| -/// ```text |
25 |
| -/// parent(i) = (i-1) / 2 |
26 |
| -/// left_child(i) = 2*i + 1 |
27 |
| -/// right_child(i) = 2*i + 2 |
28 |
| -/// ``` |
| 11 | +/// * `arr` - A mutable reference to the array to be sorted. |
| 12 | +/// * `is_max_heap` - A boolean indicating whether to build a max heap (`true`) or a min heap (`false`). |
| 13 | +fn build_heap<T: Ord>(arr: &mut [T], is_max_heap: bool) { |
| 14 | + let mut i = (arr.len() - 1) / 2; |
| 15 | + while i > 0 { |
| 16 | + heapify(arr, i, is_max_heap); |
| 17 | + i -= 1; |
| 18 | + } |
| 19 | + heapify(arr, 0, is_max_heap); |
| 20 | +} |
29 | 21 |
|
30 |
| -/// # Algorithm |
| 22 | +/// Fixes a heap violation starting at the given index. |
31 | 23 | ///
|
32 |
| -/// Heap sort has two steps: |
33 |
| -/// 1. Convert the input array to a max heap. |
34 |
| -/// 2. Partition the array into heap part and sorted part. Initially the |
35 |
| -/// heap consists of the whole array and the sorted part is empty: |
36 |
| -/// ```text |
37 |
| -/// arr: [ heap |] |
38 |
| -/// ``` |
| 24 | +/// This function adjusts the heap rooted at index `i` to fix the heap property violation. |
| 25 | +/// It assumes that the subtrees rooted at left and right children of `i` are already heaps. |
39 | 26 | ///
|
40 |
| -/// Repeatedly swap the root (i.e. the largest) element of the heap with |
41 |
| -/// the last element of the heap and increase the sorted part by one: |
42 |
| -/// ```text |
43 |
| -/// arr: [ root ... last | sorted ] |
44 |
| -/// --> [ last ... | root sorted ] |
45 |
| -/// ``` |
| 27 | +/// # Arguments |
46 | 28 | ///
|
47 |
| -/// After each swap, fix the heap to make it a valid max heap again. |
48 |
| -/// Once the heap is empty, `arr` is completely sorted. |
49 |
| -pub fn heap_sort<T: Ord>(arr: &mut [T]) { |
50 |
| - if arr.len() <= 1 { |
51 |
| - return; // already sorted |
| 29 | +/// * `arr` - A mutable reference to the array representing the heap. |
| 30 | +/// * `i` - The index to start fixing the heap violation. |
| 31 | +/// * `is_max_heap` - A boolean indicating whether to maintain a max heap or a min heap. |
| 32 | +fn heapify<T: Ord>(arr: &mut [T], i: usize, is_max_heap: bool) { |
| 33 | + let mut comparator: fn(&T, &T) -> Ordering = |a, b| a.cmp(b); |
| 34 | + if !is_max_heap { |
| 35 | + comparator = |a, b| b.cmp(a); |
52 | 36 | }
|
53 | 37 |
|
54 |
| - heapify(arr); |
| 38 | + let mut idx = i; |
| 39 | + let l = 2 * i + 1; |
| 40 | + let r = 2 * i + 2; |
55 | 41 |
|
56 |
| - for end in (1..arr.len()).rev() { |
57 |
| - arr.swap(0, end); |
58 |
| - move_down(&mut arr[..end], 0); |
| 42 | + if l < arr.len() && comparator(&arr[l], &arr[idx]) == Ordering::Greater { |
| 43 | + idx = l; |
| 44 | + } |
| 45 | + |
| 46 | + if r < arr.len() && comparator(&arr[r], &arr[idx]) == Ordering::Greater { |
| 47 | + idx = r; |
59 | 48 | }
|
60 |
| -} |
61 | 49 |
|
62 |
| -/// Convert `arr` into a max heap. |
63 |
| -fn heapify<T: Ord>(arr: &mut [T]) { |
64 |
| - let last_parent = (arr.len() - 2) / 2; |
65 |
| - for i in (0..=last_parent).rev() { |
66 |
| - move_down(arr, i); |
| 50 | + if idx != i { |
| 51 | + arr.swap(i, idx); |
| 52 | + heapify(arr, idx, is_max_heap); |
67 | 53 | }
|
68 | 54 | }
|
69 | 55 |
|
70 |
| -/// Move the element at `root` down until `arr` is a max heap again. |
| 56 | +/// Sorts the given array using heap sort algorithm. |
71 | 57 | ///
|
72 |
| -/// This assumes that the subtrees under `root` are valid max heaps already. |
73 |
| -fn move_down<T: Ord>(arr: &mut [T], mut root: usize) { |
74 |
| - let last = arr.len() - 1; |
75 |
| - loop { |
76 |
| - let left = 2 * root + 1; |
77 |
| - if left > last { |
78 |
| - break; |
79 |
| - } |
80 |
| - let right = left + 1; |
81 |
| - let max = if right <= last && arr[right] > arr[left] { |
82 |
| - right |
83 |
| - } else { |
84 |
| - left |
85 |
| - }; |
| 58 | +/// This function sorts the array either in ascending or descending order based on the `ascending` parameter. |
| 59 | +/// |
| 60 | +/// # Arguments |
| 61 | +/// |
| 62 | +/// * `arr` - A mutable reference to the array to be sorted. |
| 63 | +/// * `ascending` - A boolean indicating whether to sort in ascending order (`true`) or descending order (`false`). |
| 64 | +pub fn heap_sort<T: Ord>(arr: &mut [T], ascending: bool) { |
| 65 | + if arr.len() <= 1 { |
| 66 | + return; |
| 67 | + } |
86 | 68 |
|
87 |
| - if arr[max] > arr[root] { |
88 |
| - arr.swap(root, max); |
89 |
| - } |
90 |
| - root = max; |
| 69 | + // Build heap based on the order |
| 70 | + build_heap(arr, ascending); |
| 71 | + |
| 72 | + let mut end = arr.len() - 1; |
| 73 | + while end > 0 { |
| 74 | + arr.swap(0, end); |
| 75 | + heapify(&mut arr[..end], 0, ascending); |
| 76 | + end -= 1; |
91 | 77 | }
|
92 | 78 | }
|
93 | 79 |
|
94 | 80 | #[cfg(test)]
|
95 | 81 | mod tests {
|
96 |
| - use super::*; |
97 |
| - use crate::sorting::have_same_elements; |
98 |
| - use crate::sorting::is_sorted; |
| 82 | + use crate::sorting::{have_same_elements, heap_sort, is_descending_sorted, is_sorted}; |
99 | 83 |
|
100 |
| - #[test] |
101 |
| - fn empty() { |
102 |
| - let mut arr: Vec<i32> = Vec::new(); |
103 |
| - let cloned = arr.clone(); |
104 |
| - heap_sort(&mut arr); |
105 |
| - assert!(is_sorted(&arr) && have_same_elements(&arr, &cloned)); |
106 |
| - } |
| 84 | + macro_rules! test_heap_sort { |
| 85 | + ($($name:ident: $input:expr,)*) => { |
| 86 | + $( |
| 87 | + #[test] |
| 88 | + fn $name() { |
| 89 | + let input_array = $input; |
| 90 | + let mut arr_asc = input_array.clone(); |
| 91 | + heap_sort(&mut arr_asc, true); |
| 92 | + assert!(is_sorted(&arr_asc) && have_same_elements(&arr_asc, &input_array)); |
107 | 93 |
|
108 |
| - #[test] |
109 |
| - fn single_element() { |
110 |
| - let mut arr = vec![1]; |
111 |
| - let cloned = arr.clone(); |
112 |
| - heap_sort(&mut arr); |
113 |
| - assert!(is_sorted(&arr) && have_same_elements(&arr, &cloned)); |
114 |
| - } |
115 |
| - |
116 |
| - #[test] |
117 |
| - fn sorted_array() { |
118 |
| - let mut arr = vec![1, 2, 3, 4]; |
119 |
| - let cloned = arr.clone(); |
120 |
| - heap_sort(&mut arr); |
121 |
| - assert!(is_sorted(&arr) && have_same_elements(&arr, &cloned)); |
122 |
| - } |
123 |
| - |
124 |
| - #[test] |
125 |
| - fn unsorted_array() { |
126 |
| - let mut arr = vec![3, 4, 2, 1]; |
127 |
| - let cloned = arr.clone(); |
128 |
| - heap_sort(&mut arr); |
129 |
| - assert!(is_sorted(&arr) && have_same_elements(&arr, &cloned)); |
130 |
| - } |
131 |
| - |
132 |
| - #[test] |
133 |
| - fn odd_number_of_elements() { |
134 |
| - let mut arr = vec![3, 4, 2, 1, 7]; |
135 |
| - let cloned = arr.clone(); |
136 |
| - heap_sort(&mut arr); |
137 |
| - assert!(is_sorted(&arr) && have_same_elements(&arr, &cloned)); |
| 94 | + let mut arr_dsc = input_array.clone(); |
| 95 | + heap_sort(&mut arr_dsc, false); |
| 96 | + assert!(is_descending_sorted(&arr_dsc) && have_same_elements(&arr_dsc, &input_array)); |
| 97 | + } |
| 98 | + )* |
| 99 | + } |
138 | 100 | }
|
139 | 101 |
|
140 |
| - #[test] |
141 |
| - fn repeated_elements() { |
142 |
| - let mut arr = vec![542, 542, 542, 542]; |
143 |
| - let cloned = arr.clone(); |
144 |
| - heap_sort(&mut arr); |
145 |
| - assert!(is_sorted(&arr) && have_same_elements(&arr, &cloned)); |
| 102 | + test_heap_sort! { |
| 103 | + empty_array: Vec::<i32>::new(), |
| 104 | + single_element_array: vec![5], |
| 105 | + sorted: vec![1, 2, 3, 4, 5], |
| 106 | + sorted_desc: vec![5, 4, 3, 2, 1, 0], |
| 107 | + basic_0: vec![9, 8, 7, 6, 5], |
| 108 | + basic_1: vec![8, 3, 1, 5, 7], |
| 109 | + basic_2: vec![4, 5, 7, 1, 2, 3, 2, 8, 5, 4, 9, 9, 100, 1, 2, 3, 6, 4, 3], |
| 110 | + duplicated_elements: vec![5, 5, 5, 5, 5], |
| 111 | + strings: vec!["aa", "a", "ba", "ab"], |
146 | 112 | }
|
147 | 113 | }
|
0 commit comments