归并排序(Merge Sort)是一种高效、基于分治(Divide and Conquer)策略的排序算法。
核心思想包含三个步骤:
主要特点:
分割阶段的目标是将大数组不断对半切分,直到每个子数组只包含一个元素。这个过程是递归的。
假设我们有一个数组 `arr`,要对其 `[left, right]` 区间进行排序:
点击下方按钮查看分割动画
合并是归并排序的关键步骤。当递归返回时,我们需要将两个已排序的子数组(例如 `arr[left...mid]` 和 `arr[mid+1...right]`)合并成一个单一的、有序的数组。
合并通常使用双指针法:
归并排序的递归调用过程可以用一棵树来形象地表示。根节点是原始数组,每个非叶子节点代表一次 `mergeSort` 调用,它的两个子节点代表对其左右子数组的递归调用。叶子节点是长度为 1 的数组(天然有序)。合并操作发生在从叶子节点向上返回的过程中。
点击下方按钮生成递归树
观察这棵树可以帮助理解:
归并排序的时间复杂度可以通过递归关系式推导:T(n) = 2T(n/2) + O(n)
归并排序的主要空间开销来自于合并过程中需要使用的临时数组。
归并排序是稳定的排序算法。在合并过程中,如果遇到两个相等的元素(一个来自左子数组,一个来自右子数组),我们总是优先将左子数组中的元素放入临时数组。这样就保证了原始顺序中排在前面的相等元素,在排序后仍然排在前面。
请根据前面学到的知识,在下面的 C++ 代码框架中,补充完成递归版本的归并排序函数 `mergeSort` 和 `merge`。
// 包含必要的头文件
#include
#include
#include // std::copy
// 合并函数:将 arr[left...mid] 和 arr[mid+1...right] 合并
void merge(std::vector& arr, int left, int mid, int right) {
// TODO: 实现合并逻辑
// 1. 计算两个子数组的大小
// 2. 创建临时数组(或使用一个足够大的全局/成员临时数组)
// 3. 使用双指针合并两个子数组到临时数组
// 4. 处理剩余元素
// 5. 将临时数组的内容复制回原数组的对应区间
}
// 归并排序主函数(递归)
void mergeSort(std::vector& arr, int left, int right) {
// TODO: 实现递归分割和调用合并
// 1. 递归终止条件
// 2. 计算中间点 mid
// 3. 递归排序左半部分
// 4. 递归排序右半部分
// 5. 合并已排序的左右两部分
}
// 测试代码 (示例)
int main() {
std::vector data = {38, 27, 43, 3, 9, 82, 10};
mergeSort(data, 0, data.size() - 1);
std::cout << "Sorted array: ";
for (int x : data) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
尝试实现一个非递归版本的归并排序。这种方法通常称为自底向上(Bottom-Up)归并排序。它首先合并长度为 1 的子数组得到长度为 2 的有序子数组,然后合并长度为 2 的子数组得到长度为 4 的有序子数组,依此类推,直到整个数组有序。
#include
#include
#include // std::min, std::copy
// 复用上面的 merge 函数
// void merge(std::vector& arr, int left, int mid, int right) { ... }
void mergeSortBottomUp(std::vector& arr) {
int n = arr.size();
if (n <= 1) return;
// merge 函数需要一个临时数组,可以在这里创建一次,避免重复分配
std::vector temp(n);
// sub_len 是当前要合并的子数组长度 (1, 2, 4, 8, ...)
for (int sub_len = 1; sub_len < n; sub_len *= 2) {
// 遍历所有需要合并的相邻子数组对
// i 是每对子数组的起始索引
for (int i = 0; i < n - sub_len; i += 2 * sub_len) {
// TODO: 计算左、中、右边界,并调用 merge
// int left = i;
// int mid = ?
// int right = ?
// 注意处理边界情况,特别是最后一个子数组可能不够长
// merge(arr, left, mid, right, temp); // 假设 merge 函数接受 temp 参数
}
}
}
// 测试代码 (示例)
int main() {
std::vector data = {38, 27, 43, 3, 9, 82, 10};
mergeSortBottomUp(data);
std::cout << "Sorted array (Bottom-Up): ";
for (int x : data) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
感谢学习!希望本次讲解对你理解归并排序有所帮助。