Sorting
Merge Sort (Top-Down)
Split the array recursively, sort each half, then merge two sorted runs into one sorted result.
Algorithm
Basic Implementation
basic.rs
fn merge_sort(values: &[i32]) -> Vec<i32> {
if values.len() <= 1 {
return values.to_vec();
}
let mid = values.len() / 2;
let left = merge_sort(&values[..mid]);
let right = merge_sort(&values[mid..]);
let mut merged = Vec::new();
let (mut i, mut j) = (0, 0);
while i < left.len() && j < right.len() {
if left[i] <= right[j] {
merged.push(left[i]);
i += 1;
} else {
merged.push(right[j]);
j += 1;
}
}
merged.extend_from_slice(&left[i..]);
merged.extend_from_slice(&right[j..]);
merged
}
fn main() {
let arr = [5, 1, 4, 2, 8];
println!("{:?}", merge_sort(&arr));
}
Complexity
- Time: O(n log n)
- Space: O(n)
- Stable: yes
Implementation notes
mainstarts with a fixed array,let arr = [5, 1, 4, 2, 8], and callsmerge_sort(&arr), borrowing it as a slice.- The recursive signature is
fn merge_sort(values: &[i32]) -> Vec<i32>, so calls read borrowed slices and return newly allocated vectors rather than sorting the original array in place. - The base case
values.len() <= 1returnsvalues.to_vec(), copying the small slice into an ownedVec<i32>. - Splitting uses
let mid = values.len() / 2, then borrows&values[..mid]and&values[mid..]for the recursive calls. - Merge state is held in
let mut merged = Vec::new()and cursor indices(mut i, mut j). The comparisonleft[i] <= right[j]takes the left value first on ties, preserving stability. - Remaining elements are copied with
extend_from_slice(&left[i..])andextend_from_slice(&right[j..]); the returnedmergedvector is the sorted result. - The trace records the top split
[5, 1]and[4, 2, 8], sorted halves[1, 5]and[2, 4, 8], then merged output[1, 2, 4, 5, 8]. println!("{:?}", merge_sort(&arr))uses Rust's debug formatter for the returnedVec<i32>and prints[1, 2, 4, 5, 8].
divide and conquer
Each recursive call solves a smaller sorted subproblem.
merge step
Two sorted halves are combined by repeatedly taking the smaller front item.