Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ nalgebra = "0.34.0"
ndarray = "0.17.2"
num-bigint = { version = "0.4", optional = true }
num-traits = { version = "0.2", optional = true }
rand = "0.10"
rand = "0.10.1"

[dev-dependencies]
quickcheck = "1.0"
Expand Down
1 change: 1 addition & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@
* [Sleep Sort](https://github.com/TheAlgorithms/Rust/blob/master/src/sorting/sleep_sort.rs)
* [Sort Utils](https://github.com/TheAlgorithms/Rust/blob/master/src/sorting/sort_utils.rs)
* [Stooge Sort](https://github.com/TheAlgorithms/Rust/blob/master/src/sorting/stooge_sort.rs)
* [Strand Sort](https://github.com/TheAlgorithms/Rust/blob/master/src/sorting/strand_sort.rs)
* [Tim Sort](https://github.com/TheAlgorithms/Rust/blob/master/src/sorting/tim_sort.rs)
* [Tree Sort](https://github.com/TheAlgorithms/Rust/blob/master/src/sorting/tree_sort.rs)
* [Wave Sort](https://github.com/TheAlgorithms/Rust/blob/master/src/sorting/wave_sort.rs)
Expand Down
3 changes: 2 additions & 1 deletion src/sorting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ mod radix_sort;
mod selection_sort;
mod shell_sort;
mod sleep_sort;
#[cfg(test)]
mod sort_utils;
mod stooge_sort;
mod strand_sort;
mod tim_sort;
mod tree_sort;
mod wave_sort;
Expand Down Expand Up @@ -65,6 +65,7 @@ pub use self::selection_sort::selection_sort;
pub use self::shell_sort::shell_sort;
pub use self::sleep_sort::sleep_sort;
pub use self::stooge_sort::stooge_sort;
pub use self::strand_sort::strand_sort;
pub use self::tim_sort::tim_sort;
pub use self::tree_sort::tree_sort;
pub use self::wave_sort::wave_sort;
Expand Down
2 changes: 2 additions & 0 deletions src/sorting/sort_utils.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#[cfg(test)]
use rand::RngExt;
#[cfg(test)]
use std::time::Instant;

#[cfg(test)]
Expand Down
194 changes: 194 additions & 0 deletions src/sorting/strand_sort.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
//! # Strand Sort
//!
//! Strand Sort is a comparison-based sorting algorithm that works by repeatedly
//! extracting increasing subsequences ("strands") from the input and merging
//! them into a growing result list.
//!
//! ## Algorithm
//! 1. Remove the first element of the remaining input and start a new *strand*.
//! 2. Scan the rest of the input left-to-right; whenever an element is ≥ the
//! last element of the strand, pull it out of the input and append it to the
//! strand. One full pass yields one sorted strand.
//! 3. Merge the strand into the accumulated result via a standard two-way merge.
//! 4. Repeat until the input is empty.
//!
//! ## Complexity
//!
//! | Case | Time | Space |
//! |---------|--------|-------|
//! | Best | O(n) | O(n) |
//! | Average | O(n²) | O(n) |
//! | Worst | O(n²) | O(n) |
//!
//! The best case occurs when the input is already sorted (one strand, one merge).
//! The worst case occurs when the input is reverse-sorted (n strands of length 1).
//!
//! ## Reference
//! - [Wikipedia: Strand sort](https://en.wikipedia.org/wiki/Strand_sort)

/// Sorts a `Vec` using the Strand Sort algorithm.
///
/// Strand Sort works by repeatedly pulling increasing "strands" (already-ordered
/// subsequences) out of the input and merging them into a growing result list.
///
/// Because the algorithm relies on removing arbitrary elements mid-collection, it
/// operates on a `Vec<T>` rather than a plain slice. Linked lists would give
/// O(1) removal; `Vec` removal is O(n) per element but keeps the implementation
/// idiomatic and self-contained.
///
/// # Examples
/// ```
/// use the_algorithms_rust::sorting::strand_sort;
///
/// let mut v = vec![5, 1, 4, 2, 0, 9, 6, 3, 8, 7];
/// strand_sort(&mut v);
/// assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
/// ```
pub fn strand_sort<T: Ord>(arr: &mut Vec<T>) {
let mut result: Vec<T> = Vec::new();

while !arr.is_empty() {
// --- Build one sorted strand ---
// Move the first element of `arr` into the strand unconditionally.
let mut strand: Vec<T> = vec![arr.remove(0)];

// Walk the remaining input with an explicit index so we can remove
// elements in-place without cloning.
let mut i = 0;
while i < arr.len() {
// strand is never empty: it starts with one element and only grows.
if arr[i] >= *strand.last().unwrap() {
strand.push(arr.remove(i));
// `i` now points at the next unvisited element — do NOT advance.
} else {
i += 1;
}
}

// --- Merge the strand into the accumulated result ---
result = merge_sorted(result, strand);
}

*arr = result;
}

/// Merges two sorted `Vec`s into a single sorted `Vec`.
///
/// Consumes both inputs and produces a new vector whose length equals the sum
/// of the two input lengths. This is the standard two-way merge used in
/// merge sort, adapted here for `Vec` ownership.
fn merge_sorted<T: Ord>(left: Vec<T>, right: Vec<T>) -> Vec<T> {
let mut result = Vec::with_capacity(left.len() + right.len());
let mut left = left.into_iter().peekable();
let mut right = right.into_iter().peekable();

loop {
match (left.peek(), right.peek()) {
(Some(l), Some(r)) => {
if l <= r {
result.push(left.next().unwrap());
} else {
result.push(right.next().unwrap());
}
}
(Some(_), None) => {
result.extend(left);
break;
}
(None, Some(_)) => {
result.extend(right);
break;
}
(None, None) => break,
}
}

result
}

#[cfg(test)]
mod tests {
use super::*;
use crate::sorting::have_same_elements;
use crate::sorting::is_sorted;

#[test]
fn basic() {
let mut res = vec![10, 8, 4, 3, 1, 9, 2, 7, 5, 6];
let cloned = res.clone();
strand_sort(&mut res);
assert!(is_sorted(&res) && have_same_elements(&res, &cloned));
}

#[test]
fn basic_string() {
let mut res = vec!["d", "a", "c", "b"];
let cloned = res.clone();
strand_sort(&mut res);
assert!(is_sorted(&res) && have_same_elements(&res, &cloned));
}

#[test]
fn empty() {
let mut res: Vec<i32> = vec![];
let cloned = res.clone();
strand_sort(&mut res);
assert!(is_sorted(&res) && have_same_elements(&res, &cloned));
}

#[test]
fn one_element() {
let mut res = vec![42];
let cloned = res.clone();
strand_sort(&mut res);
assert!(is_sorted(&res) && have_same_elements(&res, &cloned));
}

#[test]
fn already_sorted() {
let mut res = vec![1, 2, 3, 4, 5];
let cloned = res.clone();
strand_sort(&mut res);
assert!(is_sorted(&res) && have_same_elements(&res, &cloned));
}

#[test]
fn reverse_sorted() {
let mut res = vec![5, 4, 3, 2, 1];
let cloned = res.clone();
strand_sort(&mut res);
assert!(is_sorted(&res) && have_same_elements(&res, &cloned));
}

#[test]
fn all_equal() {
let mut res = vec![7, 7, 7, 7];
let cloned = res.clone();
strand_sort(&mut res);
assert!(is_sorted(&res) && have_same_elements(&res, &cloned));
}

#[test]
fn duplicates() {
let mut res = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5];
let cloned = res.clone();
strand_sort(&mut res);
assert!(is_sorted(&res) && have_same_elements(&res, &cloned));
}

/// Wikipedia's own worked example: {5,1,4,2,0,9,6,3,8,7} → {0..9}
#[test]
fn wikipedia_example() {
let mut res = vec![5, 1, 4, 2, 0, 9, 6, 3, 8, 7];
strand_sort(&mut res);
assert_eq!(res, vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
}

#[test]
fn negative_numbers() {
let mut res = vec![-3, -1, -4, -1, -5, -9, -2, -6];
let cloned = res.clone();
strand_sort(&mut res);
assert!(is_sorted(&res) && have_same_elements(&res, &cloned));
}
}
Loading