递归实现就是树的后序遍历
void mergeSort_recursive(vector<int> &arr, int start, int end)
{
// 闭区间[start, end]
if (start >= end)
{
return;
}
int mid = start + ((end - start) >> 1);
// 注意(end+start)/2 当end=start+1时,mid=start,
// 因此[start,mid-1], [mid,end]这种分区方式会造成死循环,因为第二个区间大小总是不变
mergeSort_recursive(arr, start, mid);
mergeSort_recursive(arr, mid + 1, end);
// 使用临时数组合并两个已经排好序的数组
int len = end - start + 1;
vector<int> tmp(len);
int idx = 0;
for (int i = start, j = mid + 1; i <= mid || j <= end;)
{
int left = i <= mid ? arr[i] : INT_MAX;
int right = j <= end ? arr[j] : INT_MAX;
if (left <= right)
{
tmp[idx++] = arr[i++];
}
else {
tmp[idx++] = arr[j++];
}
}
// 使用临时数组覆盖相应部分
for (int i = 0; i < len; i++)
{
arr[start + i] = tmp[i];
}
}
迭代实现思路:先合并长度为1的数组,然后在合并长度为2、4、8的数组。
归并排序的迭代实现不推荐使用像快速排序那样直接使用栈模拟递归,后序遍历会麻烦很多!
void merge_sort(int arr[], int len)
{
int *a = arr;
int *b = (int *)malloc(len * sizeof(int));
int seg, start;
for (seg = 1; seg < len; seg += seg) // seg 表示合并的数组长度,从两个长度为1的开始,逐渐增大
{
for (start = 0; start < len; start += seg + seg)
{
int low = start,
mid = min(start + seg, len), // 使用min考虑区间长度不够的情况
high = min(start + seg + seg, len);
// 合并这两个数组
int k = low; // k为辅助数组的起始下标
int start1 = low, end1 = mid;
int start2 = mid, end2 = high;
while(start1 < end1 || start2 < end2){
int left = start1<end1?a[start1]:INT_MAX;
int right = start2<end2?a[start2]:INT_MAX;
if(left <=right){
b[k++]=a[start1++];
}else{
b[k++]=a[start2++];
}
}
}
// 数组在b中存着,交换a、b可以继续操作,注意的是,循环结束时,最终结果在a中
int *temp = a;
a = b;
b = temp;
}
// 可能交换了奇数次或者偶数次
if (a != arr) // 说明此时a指向的是辅助数组,b指向的是原数组
{
int i;
for (i = 0; i < len; i++)
b[i] = a[i]; // 使用辅助数组的值覆盖原数组
b = a; // b再次指向辅助数组
}
free(b);
}