This commit is contained in:
krahets 2023-10-26 22:28:34 +08:00
parent d71e2dd46b
commit 25ac5de466
2 changed files with 36 additions and 35 deletions

View file

@ -30,7 +30,7 @@ comments: true
/* 初始化数组 */ /* 初始化数组 */
// 存储在栈上 // 存储在栈上
int arr[5]; int arr[5];
int nums[5] { 1, 3, 2, 5, 4 }; int nums[5] = { 1, 3, 2, 5, 4 };
// 存储在堆上(需要手动释放空间) // 存储在堆上(需要手动释放空间)
int* arr1 = new int[5]; int* arr1 = new int[5];
int* nums1 = new int[5] { 1, 3, 2, 5, 4 }; int* nums1 = new int[5] { 1, 3, 2, 5, 4 };

View file

@ -479,45 +479,49 @@ comments: true
```rust title="merge_sort.rs" ```rust title="merge_sort.rs"
/* 合并左子数组和右子数组 */ /* 合并左子数组和右子数组 */
// 左子数组区间 [left, mid]
// 右子数组区间 [mid + 1, right]
fn merge(nums: &mut [i32], left: usize, mid: usize, right: usize) { fn merge(nums: &mut [i32], left: usize, mid: usize, right: usize) {
// 初始化辅助数组 // 左子数组区间 [left, mid], 右子数组区间 [mid+1, right]
let tmp: Vec<i32> = nums[left..right + 1].to_vec(); // 创建一个临时数组 tmp ,用于存放合并后的结果
// 左子数组的起始索引和结束索引 let tmp_size = right - left + 1;
let (left_start, left_end) = (left - left, mid - left); let mut tmp = vec![0; tmp_size];
// 右子数组的起始索引和结束索引 // 初始化左子数组和右子数组的起始索引
let (right_start, right_end) = (mid + 1 - left, right-left); let (mut i, mut j, mut k) = (left, mid + 1, 0);
// i, j 分别指向左子数组、右子数组的首元素 // 当左右子数组都还有元素时,比较并将较小的元素复制到临时数组中
let (mut l_corrent, mut r_corrent) = (left_start, right_start); while i <= mid && j <= right {
// 通过覆盖原数组 nums 来合并左子数组和右子数组 if nums[i] <= nums[j] {
for k in left..right + 1 { tmp[k] = nums[j];
// 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ i += 1;
if l_corrent > left_end { } else {
nums[k] = tmp[r_corrent]; tmp[k] = nums[j];
r_corrent += 1; j += 1;
} }
// 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ k += 1;
else if r_corrent > right_end || tmp[l_corrent] <= tmp[r_corrent] {
nums[k] = tmp[l_corrent];
l_corrent += 1;
} }
// 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ // 将左子数组和右子数组的剩余元素复制到临时数组中
else { while i <= mid {
nums[k] = tmp[r_corrent]; tmp[k] = nums[i];
r_corrent += 1; k += 1;
i += 1;
} }
while j <= right {
tmp[k] = nums[j];
k += 1;
j += 1;
}
// 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
for k in 0..tmp_size {
nums[left + k] = tmp[k];
} }
} }
/* 归并排序 */ /* 归并排序 */
fn merge_sort(left: usize, right: usize, nums: &mut [i32]) { fn merge_sort(nums: &mut [i32], left: usize, right: usize) {
// 终止条件 // 终止条件
if left >= right { return; } // 当子数组长度为 1 时终止递归 if left >= right { return; } // 当子数组长度为 1 时终止递归
// 划分阶段 // 划分阶段
let mid = (left + right) / 2; // 计算中点 let mid = (left + right) / 2; // 计算中点
merge_sort(left, mid, nums); // 递归左子数组 merge_sort(nums, left, mid); // 递归左子数组
merge_sort(mid + 1, right, nums); // 递归右子数组 merge_sort(nums, mid + 1, right); // 递归右子数组
// 合并阶段 // 合并阶段
merge(nums, left, mid, right); merge(nums, left, mid, right);
} }
@ -625,10 +629,7 @@ comments: true
} }
``` ```
实现合并函数 `merge()` 存在以下难点。 值得注意的是,`nums` 的待合并区间为 `[left, right]` ,而 `tmp` 的对应区间为 `[0, right - left]`
- **需要特别注意各个变量的含义**。`nums` 的待合并区间为 `[left, right]` ,但由于 `tmp` 仅复制了 `nums` 该区间的元素,因此 `tmp` 对应区间为 `[0, right - left]`
- 在比较 `tmp[i]``tmp[j]` 的大小时,**还需考虑子数组遍历完成后的索引越界问题**,即 `i > leftEnd``j > rightEnd` 的情况。索引越界的优先级是最高的,如果左子数组已经被合并完了,那么不需要继续比较,直接合并右子数组元素即可。
## 11.6.2 &nbsp; 算法特性 ## 11.6.2 &nbsp; 算法特性