豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content

Commit e08f5a5

Browse files
feat: add Strand Sort and fix sort_utils unused import warnings (#1036)
1 parent 2cb9392 commit e08f5a5

File tree

5 files changed

+200
-2
lines changed

5 files changed

+200
-2
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ nalgebra = "0.34.0"
99
ndarray = "0.17.2"
1010
num-bigint = { version = "0.4", optional = true }
1111
num-traits = { version = "0.2", optional = true }
12-
rand = "0.10"
12+
rand = "0.10.1"
1313

1414
[dev-dependencies]
1515
quickcheck = "1.0"

DIRECTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,7 @@
387387
* [Sleep Sort](https://github.com/TheAlgorithms/Rust/blob/master/src/sorting/sleep_sort.rs)
388388
* [Sort Utils](https://github.com/TheAlgorithms/Rust/blob/master/src/sorting/sort_utils.rs)
389389
* [Stooge Sort](https://github.com/TheAlgorithms/Rust/blob/master/src/sorting/stooge_sort.rs)
390+
* [Strand Sort](https://github.com/TheAlgorithms/Rust/blob/master/src/sorting/strand_sort.rs)
390391
* [Tim Sort](https://github.com/TheAlgorithms/Rust/blob/master/src/sorting/tim_sort.rs)
391392
* [Tree Sort](https://github.com/TheAlgorithms/Rust/blob/master/src/sorting/tree_sort.rs)
392393
* [Wave Sort](https://github.com/TheAlgorithms/Rust/blob/master/src/sorting/wave_sort.rs)

src/sorting/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ mod radix_sort;
2626
mod selection_sort;
2727
mod shell_sort;
2828
mod sleep_sort;
29-
#[cfg(test)]
3029
mod sort_utils;
3130
mod stooge_sort;
31+
mod strand_sort;
3232
mod tim_sort;
3333
mod tree_sort;
3434
mod wave_sort;
@@ -65,6 +65,7 @@ pub use self::selection_sort::selection_sort;
6565
pub use self::shell_sort::shell_sort;
6666
pub use self::sleep_sort::sleep_sort;
6767
pub use self::stooge_sort::stooge_sort;
68+
pub use self::strand_sort::strand_sort;
6869
pub use self::tim_sort::tim_sort;
6970
pub use self::tree_sort::tree_sort;
7071
pub use self::wave_sort::wave_sort;

src/sorting/sort_utils.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
#[cfg(test)]
12
use rand::RngExt;
3+
#[cfg(test)]
24
use std::time::Instant;
35

46
#[cfg(test)]

src/sorting/strand_sort.rs

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
//! # Strand Sort
2+
//!
3+
//! Strand Sort is a comparison-based sorting algorithm that works by repeatedly
4+
//! extracting increasing subsequences ("strands") from the input and merging
5+
//! them into a growing result list.
6+
//!
7+
//! ## Algorithm
8+
//! 1. Remove the first element of the remaining input and start a new *strand*.
9+
//! 2. Scan the rest of the input left-to-right; whenever an element is ≥ the
10+
//! last element of the strand, pull it out of the input and append it to the
11+
//! strand. One full pass yields one sorted strand.
12+
//! 3. Merge the strand into the accumulated result via a standard two-way merge.
13+
//! 4. Repeat until the input is empty.
14+
//!
15+
//! ## Complexity
16+
//!
17+
//! | Case | Time | Space |
18+
//! |---------|--------|-------|
19+
//! | Best | O(n) | O(n) |
20+
//! | Average | O(n²) | O(n) |
21+
//! | Worst | O(n²) | O(n) |
22+
//!
23+
//! The best case occurs when the input is already sorted (one strand, one merge).
24+
//! The worst case occurs when the input is reverse-sorted (n strands of length 1).
25+
//!
26+
//! ## Reference
27+
//! - [Wikipedia: Strand sort](https://en.wikipedia.org/wiki/Strand_sort)
28+
29+
/// Sorts a `Vec` using the Strand Sort algorithm.
30+
///
31+
/// Strand Sort works by repeatedly pulling increasing "strands" (already-ordered
32+
/// subsequences) out of the input and merging them into a growing result list.
33+
///
34+
/// Because the algorithm relies on removing arbitrary elements mid-collection, it
35+
/// operates on a `Vec<T>` rather than a plain slice. Linked lists would give
36+
/// O(1) removal; `Vec` removal is O(n) per element but keeps the implementation
37+
/// idiomatic and self-contained.
38+
///
39+
/// # Examples
40+
/// ```
41+
/// use the_algorithms_rust::sorting::strand_sort;
42+
///
43+
/// let mut v = vec![5, 1, 4, 2, 0, 9, 6, 3, 8, 7];
44+
/// strand_sort(&mut v);
45+
/// assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
46+
/// ```
47+
pub fn strand_sort<T: Ord>(arr: &mut Vec<T>) {
48+
let mut result: Vec<T> = Vec::new();
49+
50+
while !arr.is_empty() {
51+
// --- Build one sorted strand ---
52+
// Move the first element of `arr` into the strand unconditionally.
53+
let mut strand: Vec<T> = vec![arr.remove(0)];
54+
55+
// Walk the remaining input with an explicit index so we can remove
56+
// elements in-place without cloning.
57+
let mut i = 0;
58+
while i < arr.len() {
59+
// strand is never empty: it starts with one element and only grows.
60+
if arr[i] >= *strand.last().unwrap() {
61+
strand.push(arr.remove(i));
62+
// `i` now points at the next unvisited element — do NOT advance.
63+
} else {
64+
i += 1;
65+
}
66+
}
67+
68+
// --- Merge the strand into the accumulated result ---
69+
result = merge_sorted(result, strand);
70+
}
71+
72+
*arr = result;
73+
}
74+
75+
/// Merges two sorted `Vec`s into a single sorted `Vec`.
76+
///
77+
/// Consumes both inputs and produces a new vector whose length equals the sum
78+
/// of the two input lengths. This is the standard two-way merge used in
79+
/// merge sort, adapted here for `Vec` ownership.
80+
fn merge_sorted<T: Ord>(left: Vec<T>, right: Vec<T>) -> Vec<T> {
81+
let mut result = Vec::with_capacity(left.len() + right.len());
82+
let mut left = left.into_iter().peekable();
83+
let mut right = right.into_iter().peekable();
84+
85+
loop {
86+
match (left.peek(), right.peek()) {
87+
(Some(l), Some(r)) => {
88+
if l <= r {
89+
result.push(left.next().unwrap());
90+
} else {
91+
result.push(right.next().unwrap());
92+
}
93+
}
94+
(Some(_), None) => {
95+
result.extend(left);
96+
break;
97+
}
98+
(None, Some(_)) => {
99+
result.extend(right);
100+
break;
101+
}
102+
(None, None) => break,
103+
}
104+
}
105+
106+
result
107+
}
108+
109+
#[cfg(test)]
110+
mod tests {
111+
use super::*;
112+
use crate::sorting::have_same_elements;
113+
use crate::sorting::is_sorted;
114+
115+
#[test]
116+
fn basic() {
117+
let mut res = vec![10, 8, 4, 3, 1, 9, 2, 7, 5, 6];
118+
let cloned = res.clone();
119+
strand_sort(&mut res);
120+
assert!(is_sorted(&res) && have_same_elements(&res, &cloned));
121+
}
122+
123+
#[test]
124+
fn basic_string() {
125+
let mut res = vec!["d", "a", "c", "b"];
126+
let cloned = res.clone();
127+
strand_sort(&mut res);
128+
assert!(is_sorted(&res) && have_same_elements(&res, &cloned));
129+
}
130+
131+
#[test]
132+
fn empty() {
133+
let mut res: Vec<i32> = vec![];
134+
let cloned = res.clone();
135+
strand_sort(&mut res);
136+
assert!(is_sorted(&res) && have_same_elements(&res, &cloned));
137+
}
138+
139+
#[test]
140+
fn one_element() {
141+
let mut res = vec![42];
142+
let cloned = res.clone();
143+
strand_sort(&mut res);
144+
assert!(is_sorted(&res) && have_same_elements(&res, &cloned));
145+
}
146+
147+
#[test]
148+
fn already_sorted() {
149+
let mut res = vec![1, 2, 3, 4, 5];
150+
let cloned = res.clone();
151+
strand_sort(&mut res);
152+
assert!(is_sorted(&res) && have_same_elements(&res, &cloned));
153+
}
154+
155+
#[test]
156+
fn reverse_sorted() {
157+
let mut res = vec![5, 4, 3, 2, 1];
158+
let cloned = res.clone();
159+
strand_sort(&mut res);
160+
assert!(is_sorted(&res) && have_same_elements(&res, &cloned));
161+
}
162+
163+
#[test]
164+
fn all_equal() {
165+
let mut res = vec![7, 7, 7, 7];
166+
let cloned = res.clone();
167+
strand_sort(&mut res);
168+
assert!(is_sorted(&res) && have_same_elements(&res, &cloned));
169+
}
170+
171+
#[test]
172+
fn duplicates() {
173+
let mut res = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5];
174+
let cloned = res.clone();
175+
strand_sort(&mut res);
176+
assert!(is_sorted(&res) && have_same_elements(&res, &cloned));
177+
}
178+
179+
/// Wikipedia's own worked example: {5,1,4,2,0,9,6,3,8,7} → {0..9}
180+
#[test]
181+
fn wikipedia_example() {
182+
let mut res = vec![5, 1, 4, 2, 0, 9, 6, 3, 8, 7];
183+
strand_sort(&mut res);
184+
assert_eq!(res, vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
185+
}
186+
187+
#[test]
188+
fn negative_numbers() {
189+
let mut res = vec![-3, -1, -4, -1, -5, -9, -2, -6];
190+
let cloned = res.clone();
191+
strand_sort(&mut res);
192+
assert!(is_sorted(&res) && have_same_elements(&res, &cloned));
193+
}
194+
}

0 commit comments

Comments
 (0)