Skip to content

Commit

Permalink
sort: Add bottom up merge sort
Browse files Browse the repository at this point in the history
  • Loading branch information
XuShaohua committed Jul 4, 2024
1 parent 59bea58 commit c5f8283
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 4 deletions.
16 changes: 13 additions & 3 deletions sort/benches/merge_sort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@ use std::time::Duration;

use criterion::{Criterion, criterion_group, criterion_main};

use sort::merge_sort::{insertion_merge_sort, merge_sort, shell_merge_sort};
use sort::merge_sort::{
bottom_up_merge_sort, insertion_merge_sort, shell_merge_sort, topdown_merge_sort,
};
use sort::util::random_ints;

fn criterion_benchmark(c: &mut Criterion) {
for exp in 1..7 {
let len: usize = 2 * 10_usize.pow(exp);
let arr = random_ints(len).expect("Failed to generate random integers");
let title1 = format!("std_sort_for_merge_sort {len}");
let title2 = format!("merge_sort {len}");
let title2 = format!("topdown_merge_sort {len}");
let title3 = format!("insertion_merge_sort {len}");
let title4 = format!("shell_merge_sort {len}");
let title5 = format!("bottom_up_merge_sort {len}");
let mut arr_sorted = arr.clone();
arr_sorted.sort();

Expand All @@ -30,7 +33,7 @@ fn criterion_benchmark(c: &mut Criterion) {
c.bench_function(&title2, |b| {
b.iter(|| {
let mut arr2 = arr.clone();
merge_sort(&mut arr2);
topdown_merge_sort(&mut arr2);
assert_eq!(arr2, arr_sorted);
})
});
Expand All @@ -48,6 +51,13 @@ fn criterion_benchmark(c: &mut Criterion) {
assert_eq!(arr4, arr_sorted);
})
});
c.bench_function(&title5, |b| {
b.iter(|| {
let mut arr5 = arr.clone();
bottom_up_merge_sort(&mut arr5);
assert_eq!(arr5, arr_sorted);
})
});
}
}

Expand Down
16 changes: 16 additions & 0 deletions sort/src/bin/bottom_up_merge_sort.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) 2020 Xu Shaohua <[email protected]>. All rights reserved.
// Use of this source is governed by General Public License that can be found
// in the LICENSE file.

use sort::merge_sort::bottom_up_merge_sort;
use sort::util::{is_sorted, read_ints, show_brief};

fn main() {
let mut list = read_ints();
println!("[BottomUp MergeSort] list");
show_brief(&list);
bottom_up_merge_sort(&mut list);
println!("Result:");
show_brief(&list);
assert!(is_sorted(&list));
}
70 changes: 69 additions & 1 deletion sort/src/merge_sort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,46 @@ fn sort_cutoff_with_shell<T>(
}
}

/// 迭代形式的归并排序, 自底向上 bottom-up merge sort
pub fn bottom_up_merge_sort<T>(arr: &mut [T])
where
T: PartialOrd + Clone,
{
let len = arr.len();
if len < 2 {
return;
}

let mut aux = arr.to_vec();

// 开始排序的数组大小, 从 1 到 len / 2
// current_size 的取值是 1, 2, 4, 8, ...
let mut current_size = 1;

while current_size < len {
// 归并排序的数组左侧索引
let mut left_start = 0;

// 子数组的起始点不同, 这样就可以遍历整个数组.
// left_start 的取值是 0, 2 * current_size, 4 * current_size, ...
// right_end 的取值是 2 * current_size, 4 * current_size, 6 * current_size, ...
while left_start < len - 1 {
let middle = (left_start + current_size - 1).min(len - 1);
let right_end = (left_start + 2 * current_size - 1).min(len - 1);

// 合并左右两侧部分数组
merge_with_aux(arr, left_start, middle, right_end, &mut aux);

left_start += 2 * current_size;
}

current_size *= 2;
}
}

#[cfg(test)]
mod tests {
use super::{insertion_merge_sort, shell_merge_sort, topdown_merge_sort};
use super::{bottom_up_merge_sort, insertion_merge_sort, shell_merge_sort, topdown_merge_sort};

#[test]
fn test_topdown_merge_sort() {
Expand Down Expand Up @@ -290,4 +327,35 @@ mod tests {
['A', 'E', 'E', 'I', 'N', 'O', 'Q', 'S', 'S', 'T', 'U', 'Y']
);
}

#[test]
fn test_bottom_up_merge_sort() {
let mut list = [0, 5, 3, 2, 2];
bottom_up_merge_sort(&mut list);
assert_eq!(list, [0, 2, 2, 3, 5]);

let mut list = [-2, -5, -45];
bottom_up_merge_sort(&mut list);
assert_eq!(list, [-45, -5, -2]);

let mut list = [
-998_166, -996_360, -995_703, -995_238, -995_066, -994_740, -992_987, -983_833,
-987_905, -980_069, -977_640,
];
bottom_up_merge_sort(&mut list);
assert_eq!(
list,
[
-998_166, -996_360, -995_703, -995_238, -995_066, -994_740, -992_987, -987_905,
-983_833, -980_069, -977_640,
]
);

let mut list = "EASYQUESTION".chars().collect::<Vec<_>>();
bottom_up_merge_sort(&mut list);
assert_eq!(
list,
['A', 'E', 'E', 'I', 'N', 'O', 'Q', 'S', 'S', 'T', 'U', 'Y']
);
}
}
Loading

0 comments on commit c5f8283

Please sign in to comment.