diff --git a/README.md b/README.md index f1e9f13..bb835ad 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,10 @@ Genrerally, `Option::None` is an idiomatic representation of `null`. This makes - Merge sort - Bubble sort +- Insertion sort - Counting sort +- Heap sort +- Radix sort ## Graph diff --git a/src/sort.rs b/src/sort.rs index a019caf..0c33665 100644 --- a/src/sort.rs +++ b/src/sort.rs @@ -3,12 +3,13 @@ pub mod counting_sort; pub mod heap_sort; pub mod insertion_sort; pub mod merge_sort; +pub mod radix_sort; #[cfg(test)] mod tests { use super::*; #[test] - fn test_sort_not_in_place() { + fn test_out_of_placee() { let v = [10, 4, 6, 4, 8, -13, 2, 3]; let expected = [-13, 2, 3, 4, 4, 6, 8, 10]; let mut sorted; @@ -16,6 +17,14 @@ mod tests { assert_eq!(&sorted, &expected); } + #[test] + fn test_radix_sort() { + let v = [387, 468, 134, 123, 68, 221, 769, 37, 7, 890, 1, 587]; + let expected = [1, 7, 37, 68, 123, 134, 221, 387, 468, 587, 769, 890]; + let sorted = radix_sort::radix_sort(&v); + assert_eq!(&sorted, &expected); + } + #[test] fn test_sort_in_place() { let v = [10, 4, 6, 4, 8, -13, 2, 3]; diff --git a/src/sort/counting_sort.rs b/src/sort/counting_sort.rs index 0ba228e..cf05d25 100644 --- a/src/sort/counting_sort.rs +++ b/src/sort/counting_sort.rs @@ -16,17 +16,17 @@ pub fn counting_sort(v: &mut [T]) { } let sz = (max - min + T::one()).to_usize().unwrap(); - // `occurence[i]` stores how many times `i` occured in the vector to be sorted; - // later, we can then build the sorted vector by reading the `occurence` from - // left to right, pushing the value `i` to the sorted vector `occurence[i]` times. - let mut occurence = vec![0; sz]; + // `frequency[i]` stores how many times `i` occured in the vector to be sorted; + // later, we can then build the sorted vector by reading the `frequency` from + // left to right, pushing the value `i` to the sorted vector `frequency[i]` times. + let mut frequency = vec![0; sz]; for m in v.iter().map(|&n| (n - min).to_usize().unwrap()) { - occurence[m] += 1; + frequency[m] += 1; } let mut k = 0; for idx in 0..sz { let i = T::from(idx).unwrap() + min; - for _ in 0..occurence[idx] { + for _ in 0..frequency[idx] { v[k] = i; k += 1; } diff --git a/src/sort/radix_sort.rs b/src/sort/radix_sort.rs new file mode 100644 index 0000000..0419ba7 --- /dev/null +++ b/src/sort/radix_sort.rs @@ -0,0 +1,61 @@ +/// An implementation of Radix Sort. +/// +/// See https://en.wikipedia.org/wiki/Radix_sort for details on runtime and complexity Radix sorts +/// operates in O(nw) time, where n is the number of keys, and w is the key length where w is +/// constant on primitive types like Integer which gives it a better performance than other +/// compare-based sort algorithms, like i.e. QuickSort +/// +/// - Time Complexity: O(nw) + +// TODO: simplify? support negative integers? + +pub fn radix_sort(v: &[usize]) -> Vec { + if v.len() <= 1 { + return v.to_owned(); + } else { + let mx = *v.iter().max().unwrap(); + let mut ndigits = number_of_digits(mx); + let mut place = 1; + let mut a = v.to_owned(); + let mut b = vec![0; v.len()]; + let mut i = 0; + while ndigits > 0 { + if i % 2 == 0 { + counting_sort(&mut a, place, &mut b); + } else { + counting_sort(&mut b, place, &mut a); + } + ndigits -= 1; + place *= 10; + i += 1; + } + if i % 2 == 0 { + a + } else { + b + } + } +} + +fn number_of_digits(n: usize) -> usize { + (n as f64).log10() as usize + 1 +} + +fn counting_sort<'a>(v: &'a mut [usize], place: usize, sorted: &'a mut [usize]) { + const RANGE: usize = 10; + let mut frequency = vec![0; RANGE]; + let digit = v.iter().map(|n| (*n / place) % RANGE).collect::>(); + for d in &digit { + frequency[*d] += 1; + } + + for i in 1..RANGE { + // now `frequency[i]` actually represents the index in the + // sorted slice, of the next value with `i` at the relevant place + frequency[i] += frequency[i - 1]; + } + for (&n, &d) in v.iter().zip(digit.iter()).rev() { + sorted[frequency[d] - 1] = n; + frequency[d] -= 1; + } +}