1. 归并排序基础与分治思想
归并排序是计算机科学中最经典的排序算法之一,它完美体现了"分而治之"(Divide and Conquer)的算法设计思想。我第一次接触这个算法是在大学的数据结构课上,当时就被它优雅的递归实现所吸引。经过多年实际项目中的运用,我发现它不仅仅是排序工具,更是解决许多复杂问题的通用框架。
1.1 算法核心流程
归并排序的工作流程可以分为两个清晰的阶段:
分治阶段:
- 将当前数组从中间位置一分为二
- 对左半部分递归执行归并排序
- 对右半部分递归执行归并排序
合并阶段:
- 创建临时数组存放合并结果
- 使用双指针技术合并两个已排序的子数组
- 将临时数组的结果复制回原数组
这个过程中最精妙的部分在于:当数组被分解到长度为1时,它自然就是有序的,这时候开始回溯合并过程,确保每次合并的两个子数组都是已经排好序的状态。
1.2 时间复杂度分析
归并排序的时间复杂度是稳定的O(nlogn),这来自于它的递归树结构:
- 分解过程形成了一棵高度为logn的递归树
- 每层需要进行n次比较操作
- 因此总时间复杂度为O(nlogn)
空间复杂度为O(n),主要来自于合并时需要使用的临时数组。在实际工程实现中,通常会预先分配一个与原始数组等大的临时数组,避免在递归过程中频繁申请释放内存。
提示:虽然归并排序不是原地排序算法,但它的稳定性(相等元素相对位置不变)使其在需要稳定排序的场景中成为首选。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础应用:数组排序实现
让我们从最基础的数组排序开始,深入理解归并排序的实现细节。下面是一个完整的C++实现,我将逐行解析关键部分。
2.1 代码实现解析
cpp复制class Solution {
vector<int> tmp; // 预分配的临时数组
public:
vector<int> sortArray(vector<int>& nums) {
tmp.resize(nums.size()); // 一次性分配足够空间
mergeSort(nums, 0, nums.size()-1);
return nums;
}
void mergeSort(vector<int>& nums, int left, int right) {
if(left >= right) return; // 基线条件:子数组长度为1
int mid = left + (right - left)/2; // 防止溢出的中间值计算
mergeSort(nums, left, mid); // 排序左半部分
mergeSort(nums, mid+1, right);// 排序右半部分
// 合并两个有序子数组
int i = left, j = mid+1, k = 0;
while(i <= mid && j <= right) {
tmp[k++] = nums[i] <= nums[j] ? nums[i++] : nums[j++];
}
// 处理剩余元素
while(i <= mid) tmp[k++] = nums[i++];
while(j <= right) tmp[k++] = nums[j++];
// 将结果复制回原数组
for(int p = 0; p < k; p++) {
nums[left + p] = tmp[p];
}
}
};
2.2 关键细节说明
-
临时数组优化:在类成员变量中预分配临时数组,避免了递归过程中反复申请释放内存的开销。这在处理大规模数据时能显著提升性能。
-
中间值计算:使用`lef
