创建两个临时数组存储待合并的子数组
使用双指针法依次比较两个子数组的元素
将较小的元素放入原数组的对应位置
处理剩余未合并的元素
归并排序是一种采用分治法(Divide and Conquer)策略的排序算法,由约翰·冯·诺伊曼在1945年提出。它的核心思想是将一个大问题分解成若干个小问题,递归解决小问题后,再将结果合并起来。
分解:将当前区间一分为二 解决:递归地对两个子区间进行排序 合并:将两个已排序的子区间合并成一个有序区间
#include
#include
// 合并两个有序数组
void merge(int arr[], int left, int mid, int right) {
int i, j, k;
int n1 = mid - left + 1; // 左子数组长度
int n2 = right - mid; // 右子数组长度
// 创建临时数组
int *L = (int *)malloc(n1 * sizeof(int));
int *R = (int *)malloc(n2 * sizeof(int));
// 拷贝数据到临时数组
for (i = 0; i < n1; i++)
L[i] = arr[left + i];
for (j = 0; j < n2; j++)
R[j] = arr[mid + 1 + j];
i=0,j=0;
k=left;
while(i
初始调用:mergeSort(arr, 0, 3)mid = 1递归左:mergeSort(arr, 0, 1)mid = 0递归左:mergeSort(arr, 0, 0) → 直接返回递归右:mergeSort(arr, 1, 1) → 直接返回merge([38,27], 0, 0, 1) → 变为 [27, 38]递归右:mergeSort(arr, 2, 3)mid = 2递归左:mergeSort(arr, 2, 2) → 直接返回递归右:mergeSort(arr, 3, 3) → 直接返回merge([43,3], 2, 2, 3) → 变为 [3, 43]merge([27,38,3,43], 0, 1, 3) → 最终 [3, 27, 38, 43]
可能的优化
小数组优化:当子数组较小时(如 <15个元素),改用插入排序 避免重复分配内存:预先分配临时缓冲区 自底向上实现:使用迭代代替递归减少调用开销
特性 | 归并排序 | 快速排序 | 堆排序 |
---|---|---|---|
时间复杂度 | O(n log n) | O(n log n) | O(n log n) |
空间复杂度 | O(n) | O(log n) | O(1) |
稳定性 | 稳定 | 不稳定 | 不稳定 |
适用性 | 通用 | 通用 | 通用 |
最佳场景 | 大数据/外部排序 | 内存中小数据集 | 内存受限环境 |
为什么归并排序需要额外空间?
因为合并两个有序数组时需要临时存储空间,无法在原数组上直接操作而不破坏数据
如何判断归并排序是否完成?
当所有子数组都已被分解为单个元素并重新合并后,排序即完成。
归并排序在实际应用中的限制是什么?
主要限制是需要O(n)的额外空间,这在内存受限的环境中可能成为问题。
核心机制:递归分解数组至单元素后,通过有序合并逐步构建有序序列 三步骤:分界计算→双路递归→有序合并 边界处理:mid = left + (right-left)/2防溢出 空间换时间:需O(n)额外空间
该算法完美诠释了分治思想,在效率与稳定性间取得平衡,成为处理大规模数据的经典选择