Skip to content

Commit 260b448

Browse files
sozelfistvil02
andauthored
Refactor Heap Sort Implementation (#705)
* ref(sort/heap-sort): refactor heap sort - Implement generic heap sort that can sort array in both ascending and descending order. - Parametrized tests using macros - Add file and function docstrings. * ref: refactor test cases - Test cases are defined as tuples containing the input vector and the expected output vector - The `run_test_case` function runs the sorting algorithm on the input vector twice: once in ascending order and once in descending order, and then asserts that the results match the expected output vectors - The `test_heap_sort` function iterates over each test case and runs the run_test_case function for each of them * ref: update tests - Use two pre-defined methods `is_sorted` and `is_descending_sorted` to assert sorted arrays in ascending and descending orders respectively - Remove predefined outputs in tests cases * ref: rewrite tests * chore: format code --------- Co-authored-by: Piotr Idzik <[email protected]>
1 parent 1bd27f5 commit 260b448

File tree

1 file changed

+85
-119
lines changed

1 file changed

+85
-119
lines changed

src/sorting/heap_sort.rs

Lines changed: 85 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,147 +1,113 @@
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.
86
///
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.
168
///
17-
/// ... is represented by the following array:
18-
/// ```text
19-
/// 1 23 4567
20-
/// ```
9+
/// # Arguments
2110
///
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+
}
2921

30-
/// # Algorithm
22+
/// Fixes a heap violation starting at the given index.
3123
///
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.
3926
///
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
4628
///
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);
5236
}
5337

54-
heapify(arr);
38+
let mut idx = i;
39+
let l = 2 * i + 1;
40+
let r = 2 * i + 2;
5541

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;
5948
}
60-
}
6149

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);
6753
}
6854
}
6955

70-
/// Move the element at `root` down until `arr` is a max heap again.
56+
/// Sorts the given array using heap sort algorithm.
7157
///
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+
}
8668

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;
9177
}
9278
}
9379

9480
#[cfg(test)]
9581
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};
9983

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));
10793

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+
}
138100
}
139101

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"],
146112
}
147113
}

0 commit comments

Comments
 (0)