diff --git a/codes/src/algorithms/sorting/bubble-sort.md b/codes/src/algorithms/sorting/bubble-sort.md index d02a86ec..a550020c 100644 --- a/codes/src/algorithms/sorting/bubble-sort.md +++ b/codes/src/algorithms/sorting/bubble-sort.md @@ -1,13 +1,28 @@ # 冒泡排序 ```rust -pub fn bubble_sort(arr: &mut [T]) { - for i in 0..arr.len() { - for j in 0..arr.len() - 1 - i { - if arr[j] > arr[j + 1] { - arr.swap(j, j + 1); +pub fn bubble_sort(arr: &mut [T]) { + if arr.len() <= 1 { + return; + } + + let size = arr.len(); + for i in 0..(size - 1) { + // 标记当前循环是否发生元素交换 + let mut swapped = false; + + // 最后i个元素已经排好了顺序 + for j in 1..(size - i) { + if arr[j - 1] > arr[j] { + arr.swap(j - 1, j); + swapped = true; } } + + // 如果当前循环没有发生元素交换,说明数组已经有序 + if !swapped { + break; + } } } @@ -16,23 +31,37 @@ mod tests { use super::*; #[test] - fn descending() { - //降序排列 - let mut ve1 = vec![6, 5, 4, 3, 2, 1]; - bubble_sort(&mut ve1); - for i in 0..ve1.len() - 1 { - assert!(ve1[i] <= ve1[i + 1]); - } + fn test_empty_vec() { + let mut empty_vec: Vec = vec![]; + bubble_sort(&mut empty_vec); + assert_eq!(empty_vec, Vec::::new()); } #[test] - fn ascending() { - //升序,预排序 - let mut ve2 = vec![1, 2, 3, 4, 5, 6]; - bubble_sort(&mut ve2); - for i in 0..ve2.len() - 1 { - assert!(ve2[i] <= ve2[i + 1]); - } + fn test_number_vec() { + let mut vec = vec![7, 49, 73, 58, 30, 72, 44, 78, 23, 9]; + bubble_sort(&mut vec); + assert_eq!(vec, vec![7, 9, 23, 30, 44, 49, 58, 72, 73, 78]); + } + + #[test] + fn test_string_vec() { + let mut vec = vec![ + String::from("Bob"), + String::from("David"), + String::from("Carol"), + String::from("Alice"), + ]; + bubble_sort(&mut vec); + assert_eq!( + vec, + vec![ + String::from("Alice"), + String::from("Bob"), + String::from("Carol"), + String::from("David"), + ] + ); } } ``` \ No newline at end of file diff --git a/codes/src/algorithms/sorting/heap-sort.md b/codes/src/algorithms/sorting/heap-sort.md index 6e8dfff0..48ab5025 100644 --- a/codes/src/algorithms/sorting/heap-sort.md +++ b/codes/src/algorithms/sorting/heap-sort.md @@ -1,96 +1,38 @@ # 堆排序 ```rust -/// Sort a mutable slice using heap sort. -/// -/// Heap sort is an in-place O(n log n) sorting algorithm. It is based on a -/// max heap, a binary tree data structure whose main feature is that -/// parent nodes are always greater or equal to their child nodes. -/// -/// # Max Heap Implementation -/// -/// A max heap can be efficiently implemented with an array. -/// For example, the binary tree: -/// ```text -/// 1 -/// 2 3 -/// 4 5 6 7 -/// ``` -/// -/// ... is represented by the following array: -/// ```text -/// 1 23 4567 -/// ``` -/// -/// Given the index `i` of a node, parent and child indices can be calculated -/// as follows: -/// ```text -/// parent(i) = (i-1) / 2 -/// left_child(i) = 2*i + 1 -/// right_child(i) = 2*i + 2 -/// ``` - -/// # Algorithm -/// -/// Heap sort has two steps: -/// 1. Convert the input array to a max heap. -/// 2. Partition the array into heap part and sorted part. Initially the -/// heap consists of the whole array and the sorted part is empty: -/// ```text -/// arr: [ heap |] -/// ``` -/// -/// Repeatedly swap the root (i.e. the largest) element of the heap with -/// the last element of the heap and increase the sorted part by one: -/// ```text -/// arr: [ root ... last | sorted ] -/// --> [ last ... | root sorted ] -/// ``` -/// -/// After each swap, fix the heap to make it a valid max heap again. -/// Once the heap is empty, `arr` is completely sorted. -pub fn heap_sort(arr: &mut [T]) { - if arr.len() <= 1 { - return; // already sorted +pub fn heap_sort(arr: &mut [T]) { + let size = arr.len(); + // 构建大根堆 + for i in (0..size / 2).rev() { + heapify(arr, i, size); } - heapify(arr); - - for end in (1..arr.len()).rev() { - arr.swap(0, end); - move_down(&mut arr[..end], 0); + // 每轮循环将堆顶元素(也就是最大元素)放到最后 + for i in (1..size).rev() { + arr.swap(0, i); + // 恢复大根堆 + heapify(arr, 0, i); } } -/// Convert `arr` into a max heap. -fn heapify(arr: &mut [T]) { - let last_parent = (arr.len() - 2) / 2; - for i in (0..=last_parent).rev() { - move_down(arr, i); +fn heapify(arr: &mut [T], root: usize, end: usize) { + // 记录父节点和左右节点中最大元素的索引位置 + let mut largest = root; + + let left_child = 2 * root + 1; + if left_child < end && arr[left_child] > arr[largest] { + largest = left_child; } -} -/// Move the element at `root` down until `arr` is a max heap again. -/// -/// This assumes that the subtrees under `root` are valid max heaps already. -fn move_down(arr: &mut [T], mut root: usize) { - let last = arr.len() - 1; - loop { - let left = 2 * root + 1; - if left > last { - break; - } - let right = left + 1; - let max = if right <= last && arr[right] > arr[left] { - right - } else { - left - }; + let right_child = left_child + 1; + if right_child < end && arr[right_child] > arr[largest] { + largest = right_child; + } - if arr[max] > arr[root] { - arr.swap(root, max); - } - root = max; + if largest != root { + arr.swap(root, largest); + heapify(arr, largest, end); } } @@ -99,45 +41,37 @@ mod tests { use super::*; #[test] - fn empty() { - let mut arr: Vec = Vec::new(); - heap_sort(&mut arr); - assert_eq!(&arr, &[]); - } - - #[test] - fn single_element() { - let mut arr = vec![1]; - heap_sort(&mut arr); - assert_eq!(&arr, &[1]); - } - - #[test] - fn sorted_array() { - let mut arr = vec![1, 2, 3, 4]; - heap_sort(&mut arr); - assert_eq!(&arr, &[1, 2, 3, 4]); - } - - #[test] - fn unsorted_array() { - let mut arr = vec![3, 4, 2, 1]; - heap_sort(&mut arr); - assert_eq!(&arr, &[1, 2, 3, 4]); + fn test_empty_vec() { + let mut empty_vec: Vec = vec![]; + heap_sort(&mut empty_vec); + assert_eq!(empty_vec, Vec::::new()); } #[test] - fn odd_number_of_elements() { - let mut arr = vec![3, 4, 2, 1, 7]; - heap_sort(&mut arr); - assert_eq!(&arr, &[1, 2, 3, 4, 7]); + fn test_number_vec() { + let mut vec = vec![7, 49, 73, 58, 30, 72, 44, 78, 23, 9]; + heap_sort(&mut vec); + assert_eq!(vec, vec![7, 9, 23, 30, 44, 49, 58, 72, 73, 78]); } #[test] - fn repeated_elements() { - let mut arr = vec![542, 542, 542, 542]; - heap_sort(&mut arr); - assert_eq!(&arr, &vec![542, 542, 542, 542]); + fn test_string_vec() { + let mut vec = vec![ + String::from("Bob"), + String::from("David"), + String::from("Carol"), + String::from("Alice"), + ]; + heap_sort(&mut vec); + assert_eq!( + vec, + vec![ + String::from("Alice"), + String::from("Bob"), + String::from("Carol"), + String::from("David"), + ] + ); } } ``` \ No newline at end of file diff --git a/codes/src/algorithms/sorting/insertion-sort.md b/codes/src/algorithms/sorting/insertion-sort.md index 6b94e3d1..061fce0d 100644 --- a/codes/src/algorithms/sorting/insertion-sort.md +++ b/codes/src/algorithms/sorting/insertion-sort.md @@ -1,25 +1,27 @@ # 插入排序 ```rust -use std::cmp; - -/// Sorts a mutable slice using in-place insertion sort algorithm. -/// -/// Time complexity is `O(n^2)`, where `n` is the number of elements. -/// Space complexity is `O(1)` as it sorts elements in-place. -pub fn insertion_sort(arr: &mut [T]) -where - T: cmp::PartialOrd + Copy, -{ +pub fn insertion_sort(arr: &mut [T]) { + // 从第二个元素开始排序 for i in 1..arr.len() { - let cur = arr[i]; - let mut j = i - 1; + // 找到 arr[i] 该插入的位置 + let mut j = i; + while j > 0 && arr[j - 1] > arr[j] { + arr.swap(j - 1, j); + j -= 1; + } + } +} - while arr[j] > cur { - arr.swap(j + 1, j); - if j == 0 { - break; - } +// 这里需要 T: Ord 是因为 binary_search() 方法的限制 +pub fn insertion_sort_binary_search(arr: &mut[T]) { + // 从第二个元素开始排序 + for i in 1..arr.len() { + // 利用二分查找获取 arr[i] 应该插入的位置 + let pos = arr[..i].binary_search(&arr[i]).unwrap_or_else(|pos| pos); + let mut j = i; + while j > pos { + arr.swap(j - 1, j); j -= 1; } } @@ -27,49 +29,82 @@ where #[cfg(test)] mod tests { - use super::super::is_sorted; use super::*; - #[test] - fn empty() { - let mut arr: [u8; 0] = []; - insertion_sort(&mut arr); - assert!(is_sorted(&arr)); - } + mod insertion_sort { + use super::*; - #[test] - fn one_element() { - let mut arr: [char; 1] = ['a']; - insertion_sort(&mut arr); - assert!(is_sorted(&arr)); - } - - #[test] - fn already_sorted() { - let mut arr: [&str; 3] = ["a", "b", "c"]; - insertion_sort(&mut arr); - assert!(is_sorted(&arr)); - } - - #[test] - fn basic() { - let mut arr: [&str; 4] = ["d", "a", "c", "b"]; - insertion_sort(&mut arr); - assert!(is_sorted(&arr)); - } - - #[test] - fn odd_number_of_elements() { - let mut arr: Vec<&str> = vec!["d", "a", "c", "e", "b"]; - insertion_sort(&mut arr); - assert!(is_sorted(&arr)); + #[test] + fn test_empty_vec() { + let mut empty_vec: Vec = vec![]; + insertion_sort(&mut empty_vec); + assert_eq!(empty_vec, Vec::::new()); + } + + #[test] + fn test_number_vec() { + let mut vec = vec![7, 49, 73, 58, 30, 72, 44, 78, 23, 9]; + insertion_sort(&mut vec); + assert_eq!(vec, vec![7, 9, 23, 30, 44, 49, 58, 72, 73, 78]); + } + + #[test] + fn test_string_vec() { + let mut vec = vec![ + String::from("Bob"), + String::from("David"), + String::from("Carol"), + String::from("Alice"), + ]; + insertion_sort(&mut vec); + assert_eq!( + vec, + vec![ + String::from("Alice"), + String::from("Bob"), + String::from("Carol"), + String::from("David"), + ] + ); + } } - #[test] - fn repeated_elements() { - let mut arr: Vec = vec![542, 542, 542, 542]; - insertion_sort(&mut arr); - assert!(is_sorted(&arr)); + mod insertion_sort_binary_search { + use super::*; + + #[test] + fn test_empty_vec() { + let mut empty_vec: Vec = vec![]; + insertion_sort_binary_search(&mut empty_vec); + assert_eq!(empty_vec, Vec::::new()); + } + + #[test] + fn test_number_vec() { + let mut vec = vec![7, 49, 73, 58, 30, 72, 44, 78, 23, 9]; + insertion_sort_binary_search(&mut vec); + assert_eq!(vec, vec![7, 9, 23, 30, 44, 49, 58, 72, 73, 78]); + } + + #[test] + fn test_string_vec() { + let mut vec = vec![ + String::from("Bob"), + String::from("David"), + String::from("Carol"), + String::from("Alice"), + ]; + insertion_sort_binary_search(&mut vec); + assert_eq!( + vec, + vec![ + String::from("Alice"), + String::from("Bob"), + String::from("Carol"), + String::from("David"), + ] + ); + } } } ``` \ No newline at end of file diff --git a/codes/src/algorithms/sorting/merge-sort.md b/codes/src/algorithms/sorting/merge-sort.md index 00b5c404..c215f732 100644 --- a/codes/src/algorithms/sorting/merge-sort.md +++ b/codes/src/algorithms/sorting/merge-sort.md @@ -1,64 +1,56 @@ # 归并排序 ```rust -fn _merge(arr: &mut [T], lo: usize, mid: usize, hi: usize) { - // create temporary arrays to support merge - let mut left_half = Vec::new(); - let mut right_half = Vec::new(); - for v in arr.iter().take(mid + 1).skip(lo) { - left_half.push(*v); - } - for v in arr.iter().take(hi + 1).skip(mid + 1) { - right_half.push(*v); +pub fn merge_sort(arr: &mut [T]) +where + T: PartialOrd + Clone + Default, +{ + if arr.len() > 1 { + merge_sort_range(arr, 0, arr.len() - 1); } +} - let lsize = left_half.len(); - let rsize = right_half.len(); +fn merge_sort_range(arr: &mut [T], lo: usize, hi: usize) +where + T: PartialOrd + Clone + Default, +{ + // 只有当元素个数大于一时才进行排序 + if lo < hi { + let mid = lo + ((hi - lo) >> 1); + merge_sort_range(arr, lo, mid); + merge_sort_range(arr, mid + 1, hi); + merge_two_arrays(arr, lo, mid, hi); + } +} - // pointers to track the positions while merging - let mut l = 0; - let mut r = 0; - let mut a = lo; +// 合并两个有序数组: arr[lo..=mid], arr[mid + 1..=hi] +fn merge_two_arrays(arr: &mut [T], lo: usize, mid: usize, hi: usize) +where + T: PartialOrd + Clone + Default, +{ + // 这里需要 clone 数组元素 + let mut arr1 = arr[lo..=mid].to_vec(); + let mut arr2 = arr[mid + 1..=hi].to_vec(); - // pick smaller element one by one from either left or right half - while l < lsize && r < rsize { - if left_half[l] < right_half[r] { - arr[a] = left_half[l]; - l += 1; + let (mut i, mut j) = (0, 0); + while i < arr1.len() && j < arr2.len() { + if arr1[i] < arr2[j] { + arr[i + j + lo] = std::mem::take(&mut arr1[i]); + i += 1; } else { - arr[a] = right_half[r]; - r += 1; + arr[i + j + lo] = std::mem::take(&mut arr2[j]); + j += 1; } - a += 1; } - // put all the remaining ones - while l < lsize { - arr[a] = left_half[l]; - l += 1; - a += 1; + while i < arr1.len() { + arr[i + j + lo] = std::mem::take(&mut arr1[i]); + i += 1; } - while r < rsize { - arr[a] = right_half[r]; - r += 1; - a += 1; - } -} - -fn _merge_sort(arr: &mut [T], lo: usize, hi: usize) { - if lo < hi { - let mid = lo + (hi - lo) / 2; - _merge_sort(arr, lo, mid); - _merge_sort(arr, mid + 1, hi); - _merge(arr, lo, mid, hi); - } -} - -pub fn merge_sort(arr: &mut [T]) { - let len = arr.len(); - if len > 1 { - _merge_sort(arr, 0, len - 1); + while j < arr2.len() { + arr[i + j + lo] = std::mem::take(&mut arr2[j]); + j += 1; } } @@ -67,45 +59,37 @@ mod tests { use super::*; #[test] - fn basic() { - let mut res = vec![10, 8, 4, 3, 1, 9, 2, 7, 5, 6]; - merge_sort(&mut res); - assert_eq!(res, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); - } - - #[test] - fn basic_string() { - let mut res = vec!["a", "bb", "d", "cc"]; - merge_sort(&mut res); - assert_eq!(res, vec!["a", "bb", "cc", "d"]); - } - - #[test] - fn empty() { - let mut res = Vec::::new(); - merge_sort(&mut res); - assert_eq!(res, vec![]); - } - - #[test] - fn one_element() { - let mut res = vec![1]; - merge_sort(&mut res); - assert_eq!(res, vec![1]); + fn test_empty_vec() { + let mut empty_vec: Vec = vec![]; + merge_sort(&mut empty_vec); + assert_eq!(empty_vec, Vec::::new()); } #[test] - fn pre_sorted() { - let mut res = vec![1, 2, 3, 4]; - merge_sort(&mut res); - assert_eq!(res, vec![1, 2, 3, 4]); + fn test_number_vec() { + let mut vec = vec![7, 49, 73, 58, 30, 72, 44, 78, 23, 9]; + merge_sort(&mut vec); + assert_eq!(vec, vec![7, 9, 23, 30, 44, 49, 58, 72, 73, 78]); } #[test] - fn reverse_sorted() { - let mut res = vec![4, 3, 2, 1]; - merge_sort(&mut res); - assert_eq!(res, vec![1, 2, 3, 4]); + fn test_string_vec() { + let mut vec = vec![ + String::from("Bob"), + String::from("David"), + String::from("Carol"), + String::from("Alice"), + ]; + merge_sort(&mut vec); + assert_eq!( + vec, + vec![ + String::from("Alice"), + String::from("Bob"), + String::from("Carol"), + String::from("David"), + ] + ); } } ``` \ No newline at end of file diff --git a/codes/src/algorithms/sorting/quick-sort.md b/codes/src/algorithms/sorting/quick-sort.md index 2e0b7b1e..32175206 100644 --- a/codes/src/algorithms/sorting/quick-sort.md +++ b/codes/src/algorithms/sorting/quick-sort.md @@ -1,40 +1,125 @@ # 快速排序 ```rust -use std::cmp::PartialOrd; +pub fn quick_sort(arr: &mut [T]) { + if arr.len() > 1 { + quick_sort_range(arr, 0, arr.len() - 1); + } +} + +fn quick_sort_range(arr: &mut [T], lo: usize, hi: usize) { + // 只有当元素个数大于一时才进行排序 + if lo < hi { + let pos = partition(arr, lo, hi); + // let pos = partition_random(arr, lo, hi); + if pos != 0 { + // 如果 pos == 0, pos - 1 会发生溢出错误 + quick_sort_range(arr, lo, pos - 1); + } + quick_sort_range(arr, pos + 1, hi); + } +} -pub fn partition(arr: &mut [T], lo: isize, hi: isize) -> isize { - let pivot = hi as usize; - let mut i = lo - 1; - let mut j = hi; +fn partition(arr: &mut [T], lo: usize, hi: usize) -> usize { + // 默认选取 lo 作为 pivot + let pivot = lo; - loop { - i += 1; - while arr[i as usize] < arr[pivot] { - i += 1; + let (mut left, mut right) = (lo, hi); + while left < right { + // 找到右边第一个不大于等于 arr[pivot] 的元素 + while left < right && arr[right] >= arr[pivot] { + right -= 1; } - j -= 1; - while j >= 0 && arr[j as usize] > arr[pivot] { - j -= 1; + + // 找到左边第一个不小于等于 arr[pivot] 的元素 + while left < right && arr[left] <= arr[pivot] { + left += 1; } - if i >= j { - break; - } else { - arr.swap(i as usize, j as usize); + + // 交换前面找到的两个元素 + if left != right { + arr.swap(left, right); } } - arr.swap(i as usize, pivot as usize); - i + + arr.swap(pivot, left); + + // 返回正确的分割位置 + left } -fn _quick_sort(arr: &mut [T], lo: isize, hi: isize) { - if lo < hi { - let p = partition(arr, lo, hi); - _quick_sort(arr, lo, p - 1); - _quick_sort(arr, p + 1, hi); + +// 随机选取 pivot 的位置 +fn partition_random(arr: &mut [T], lo: usize, hi: usize) -> usize { + // 在 Cargo.toml 的依赖中添加 rand 库 + use rand::Rng; + let mut rng = rand::thread_rng(); + let pivot = rng.gen_range(lo..=hi); + + // 交换 lo 和 pivot 位置上的元素,从而间接使得 pivot = lo + // 因此后序操作和 partition() 函数一致 + arr.swap(lo, pivot); + + let pivot = lo; + let (mut left, mut right) = (lo, hi); + while left < right { + // 找到右边第一个不大于等于 arr[pivot] 的元素 + while left < right && arr[right] >= arr[pivot] { + right -= 1; + } + + // 找到左边第一个不小于等于 arr[pivot] 的元素 + while left < right && arr[left] <= arr[pivot] { + left += 1; + } + + // 交换前面找到的两个元素 + if left != right { + arr.swap(left, right); + } } + + arr.swap(pivot, left); + + // 返回正确的分割位置 + left } -pub fn quick_sort(arr: &mut [T]) { - let len = arr.len(); - _quick_sort(arr, 0, (len - 1) as isize); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_empty_vec() { + let mut empty_vec: Vec = vec![]; + quick_sort(&mut empty_vec); + assert_eq!(empty_vec, Vec::::new()); + } + + #[test] + fn test_number_vec() { + let mut vec = vec![7, 49, 73, 58, 30, 72, 44, 78, 23, 9]; + quick_sort(&mut vec); + assert_eq!(vec, vec![7, 9, 23, 30, 44, 49, 58, 72, 73, 78]); + } + + #[test] + fn test_string_vec() { + let mut vec = vec![ + String::from("Bob"), + String::from("David"), + String::from("Carol"), + String::from("Alice"), + ]; + quick_sort(&mut vec); + assert_eq!( + vec, + vec![ + String::from("Alice"), + String::from("Bob"), + String::from("Carol"), + String::from("David"), + ] + ); + } } ``` \ No newline at end of file diff --git a/codes/src/algorithms/sorting/selection-sort.md b/codes/src/algorithms/sorting/selection-sort.md index 73b3dcfa..bd2c682d 100644 --- a/codes/src/algorithms/sorting/selection-sort.md +++ b/codes/src/algorithms/sorting/selection-sort.md @@ -1,16 +1,24 @@ # 选择排序 ```rust -pub fn selection_sort(arr: &mut [T]) { - let len = arr.len(); - for left in 0..len { - let mut smallest = left; - for right in (left + 1)..len { - if arr[right] < arr[smallest] { - smallest = right; +pub fn selection_sort(arr: &mut [T]) { + if arr.len() <= 1 { + return; + } + + let size = arr.len(); + for i in 0..(size - 1) { + // 找到最小元素的索引值 + let mut min_index = i; + for j in (i + 1)..size { + if arr[j] < arr[min_index] { + min_index = j; } } - arr.swap(smallest, left); + + if min_index != i { + arr.swap(i, min_index); + } } } @@ -19,31 +27,37 @@ mod tests { use super::*; #[test] - fn basic() { - let mut res = vec!["d", "a", "c", "b"]; - selection_sort(&mut res); - assert_eq!(res, vec!["a", "b", "c", "d"]); - } - - #[test] - fn empty() { - let mut res = Vec::::new(); - selection_sort(&mut res); - assert_eq!(res, vec![]); + fn test_empty_vec() { + let mut empty_vec: Vec = vec![]; + selection_sort(&mut empty_vec); + assert_eq!(empty_vec, Vec::::new()); } #[test] - fn one_element() { - let mut res = vec!["a"]; - selection_sort(&mut res); - assert_eq!(res, vec!["a"]); + fn test_number_vec() { + let mut vec = vec![7, 49, 73, 58, 30, 72, 44, 78, 23, 9]; + selection_sort(&mut vec); + assert_eq!(vec, vec![7, 9, 23, 30, 44, 49, 58, 72, 73, 78]); } #[test] - fn pre_sorted() { - let mut res = vec!["a", "b", "c"]; - selection_sort(&mut res); - assert_eq!(res, vec!["a", "b", "c"]); + fn test_string_vec() { + let mut vec = vec![ + String::from("Bob"), + String::from("David"), + String::from("Carol"), + String::from("Alice"), + ]; + selection_sort(&mut vec); + assert_eq!( + vec, + vec![ + String::from("Alice"), + String::from("Bob"), + String::from("Carol"), + String::from("David"), + ] + ); } } ``` \ No newline at end of file