-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #32 from KeplerOps/feature/issue-21-selectionsort
Issue #21: Implement Selection Sort
- Loading branch information
Showing
2 changed files
with
211 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
205 changes: 205 additions & 0 deletions
205
crates/cs/blocks-cs-sort/src/algorithms/selectionsort.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
use std::fmt::Debug; | ||
|
||
/// Selection Sort implementation for sorting slices. | ||
/// | ||
/// # Algorithm Overview | ||
/// Selection sort works by: | ||
/// 1. Dividing the input into a sorted and unsorted region | ||
/// 2. Finding the minimum element in the unsorted region | ||
/// 3. Swapping it with the first element of the unsorted region | ||
/// 4. Moving the boundary between sorted and unsorted regions one element to the right | ||
/// | ||
/// # Time Complexity | ||
/// - Best Case: O(n²) | ||
/// - Average Case: O(n²) | ||
/// - Worst Case: O(n²) | ||
/// | ||
/// # Space Complexity | ||
/// - O(1) auxiliary space | ||
/// | ||
/// # Stability | ||
/// - Not stable by default (equal elements may change relative order) | ||
/// | ||
/// # Advantages | ||
/// - Simple implementation | ||
/// - Performs well on small arrays | ||
/// - Minimizes the number of swaps (O(n) swaps vs O(n²) comparisons) | ||
/// - In-place algorithm | ||
/// - Works well when memory writes are expensive | ||
pub fn sort<T>(slice: &mut [T]) | ||
where | ||
T: Ord + Clone + Debug, | ||
{ | ||
let len = slice.len(); | ||
|
||
// One by one move boundary of unsorted subarray | ||
for i in 0..len { | ||
// Find the minimum element in unsorted array | ||
let mut min_idx = i; | ||
for j in (i + 1)..len { | ||
if slice[j] < slice[min_idx] { | ||
min_idx = j; | ||
} | ||
} | ||
|
||
// Swap the found minimum element with the first element | ||
// Only if we found a smaller element | ||
if min_idx != i { | ||
slice.swap(i, min_idx); | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_empty_slice() { | ||
let mut arr: Vec<i32> = vec![]; | ||
sort(&mut arr); | ||
assert_eq!(arr, vec![]); | ||
} | ||
|
||
#[test] | ||
fn test_single_element() { | ||
let mut arr = vec![1]; | ||
sort(&mut arr); | ||
assert_eq!(arr, vec![1]); | ||
} | ||
|
||
#[test] | ||
fn test_sorted_array() { | ||
let mut arr = vec![1, 2, 3, 4, 5]; | ||
sort(&mut arr); | ||
assert_eq!(arr, vec![1, 2, 3, 4, 5]); | ||
} | ||
|
||
#[test] | ||
fn test_reverse_sorted() { | ||
let mut arr = vec![5, 4, 3, 2, 1]; | ||
sort(&mut arr); | ||
assert_eq!(arr, vec![1, 2, 3, 4, 5]); | ||
} | ||
|
||
#[test] | ||
fn test_random_order() { | ||
let mut arr = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]; | ||
let mut expected = arr.clone(); | ||
expected.sort(); | ||
sort(&mut arr); | ||
assert_eq!(arr, expected); | ||
} | ||
|
||
#[test] | ||
fn test_duplicate_elements() { | ||
let mut arr = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]; | ||
let mut expected = arr.clone(); | ||
expected.sort(); | ||
sort(&mut arr); | ||
assert_eq!(arr, expected); | ||
} | ||
|
||
#[test] | ||
fn test_large_array() { | ||
let mut arr: Vec<i32> = (0..1000).rev().collect(); | ||
let mut expected = arr.clone(); | ||
expected.sort(); | ||
sort(&mut arr); | ||
assert_eq!(arr, expected); | ||
} | ||
|
||
#[test] | ||
fn test_different_types() { | ||
// Test with floating point numbers | ||
let mut float_arr = vec![3.14, 1.41, 2.71, 0.58]; | ||
let mut expected = float_arr.clone(); | ||
expected.sort_by(|a, b| a.partial_cmp(b).unwrap()); | ||
sort(&mut float_arr); | ||
assert_eq!(float_arr, expected); | ||
|
||
// Test with strings | ||
let mut string_arr = vec!["banana", "apple", "cherry", "date"]; | ||
let mut expected = string_arr.clone(); | ||
expected.sort(); | ||
sort(&mut string_arr); | ||
assert_eq!(string_arr, expected); | ||
} | ||
|
||
#[test] | ||
fn test_all_equal() { | ||
// Test with an array where all elements are equal | ||
let mut arr = vec![1, 1, 1, 1, 1]; | ||
sort(&mut arr); | ||
assert_eq!(arr, vec![1, 1, 1, 1, 1]); | ||
} | ||
|
||
#[test] | ||
fn test_alternating() { | ||
// Test with an array that alternates between two values | ||
let mut arr = vec![1, 2, 1, 2, 1, 2]; | ||
sort(&mut arr); | ||
assert_eq!(arr, vec![1, 1, 1, 2, 2, 2]); | ||
} | ||
|
||
#[test] | ||
fn test_negative_numbers() { | ||
// Test with negative numbers | ||
let mut arr = vec![-5, -2, -8, -1, -9]; | ||
sort(&mut arr); | ||
assert_eq!(arr, vec![-9, -8, -5, -2, -1]); | ||
} | ||
|
||
#[test] | ||
fn test_mixed_numbers() { | ||
// Test with a mix of positive and negative numbers | ||
let mut arr = vec![-3, 4, 0, -1, 5, -2]; | ||
sort(&mut arr); | ||
assert_eq!(arr, vec![-3, -2, -1, 0, 4, 5]); | ||
} | ||
|
||
#[test] | ||
fn test_minimal_swaps() { | ||
// Test that we're making O(n) swaps | ||
let mut count = 0; | ||
let mut arr = vec![5, 4, 3, 2, 1]; | ||
|
||
// Wrap the slice in a type that counts swaps | ||
struct SwapCounter<'a, T> { | ||
slice: &'a mut [T], | ||
swap_count: &'a mut usize, | ||
} | ||
|
||
impl<'a, T> SwapCounter<'a, T> { | ||
fn swap(&mut self, i: usize, j: usize) { | ||
self.slice.swap(i, j); | ||
*self.swap_count += 1; | ||
} | ||
} | ||
|
||
{ | ||
let mut counter = SwapCounter { | ||
slice: &mut arr, | ||
swap_count: &mut count, | ||
}; | ||
|
||
// Custom selection sort that uses the counter | ||
for i in 0..counter.slice.len() { | ||
let mut min_idx = i; | ||
for j in (i + 1)..counter.slice.len() { | ||
if counter.slice[j] < counter.slice[min_idx] { | ||
min_idx = j; | ||
} | ||
} | ||
if min_idx != i { | ||
counter.swap(i, min_idx); | ||
} | ||
} | ||
} | ||
|
||
// For n=5, we expect at most n-1=4 swaps in selection sort | ||
assert!(count <= arr.len() - 1, | ||
"Expected at most {} swaps, but got {}", arr.len() - 1, count); | ||
assert_eq!(arr, vec![1, 2, 3, 4, 5]); | ||
} | ||
} |