归并排序是一种效率非常高的并且稳定的排序算法,借助临时数组,其平均时间复杂度为O(nlogn)。
类似于快速排序,归并排序也是使用分治法将数组逐步分割成更小的数组进行排序。但快速排序是采用枢纽元将数组的元素分成左右两部分,这是导致不稳定的原因所在。而归并排序则是逐步将两个有序数组合并成一个新的有序数组,对于值相等的元素,它们的相对位置是不会改变的,因此算法是稳定的。
下面通过一个例子来说明归并排序:
原数组:4 6 5 8 7 2 1 3
(1)将原数组分割成两个子数组,分别为4 6 5 8和7 2 1 3
(2)将子数组4 6 5 8进行分割,变成4 6和5 8
(3)将子数组4 6进行分割,变成4和6,将子数组5 8进行分割,变成5和8
(4)将4和6合并成有序数组,即4 6,将5和8合并成有序数组,即5 8
(5)将4 6和5 8合并成有序数组,从两个子数组的数组头开始:4和5较小的数是4,取出4放到临时数组中,然后将第一个子数组的指针往右移一位;6和5较小的数是5,取出5放到临时数组中,然后将第二个子数组的指针往右移一位;6和8较小的数是6,取出6放到临时数组中,然后将第一个子数组的指针往右移一位。此时第一个数组已经到结尾,因此把第二个数组接到临时数组当前指针的后面。最后把临时数组复制回原数组。即4 5 6 8
(6)等同于(2)~(5),将7 2 1 3进行排序,变成1 2 3 7
(7)对两个有序的子数组4 5 6 8和1 2 3 7按第(5)步的方式进行合并,得到最终的有序数组1 2 3 4 5 6 7 8
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 100005; //数组大小
int num[N];
int temp[N];
int n;
//归并成有序数组
void merge(int low, int mid, int high)
{
int i = low; //左半部分当前位置
int j = mid + 1; //右半部分当前位置
int k = low; //结果数组当前位置
while (i <= mid && j <= high) // 归并,每次找两边数组当前元素的较小者加入结果数组中
{
if (num[i] <= num[j]) temp[k++] = num[i++];
else temp[k++] = num[j++];
}
while (i <= mid) temp[k++] = num[i++]; //把左半部分数组剩余部分加入结果数组
while (j <= high) temp[k++] = num[j++]; //把右半部分数组剩余部分加入结果数组
for (i = low; i <= high; ++i) num[i] = temp[i]; //从结果数组中把元素复制回原数组
}
//递归地进行归并排序
void mergeSort(int beg, int end)
{
if (beg < end) //两个端点不重合
{
int mid = (beg + end) / 2; //获取中点
mergeSort(beg, mid); //对左半部分进行排序
mergeSort(mid + 1, end); //对右半部分进行排序
merge(beg, mid, end); //合并左右两部分
}
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; ++i)
{
scanf("%d", num + i);
}
mergeSort(0, n - 1);
for (int i = 0; i < n; ++i)
{
printf("%d ", num[i]);
}
printf("\n");
return 0;
}