原理:归并排序就是利用递归和分治,将数据序列划分为越来越小的半子表,再对半子表排序,最后再用递归将排序好的半子表合并成越来越大的有序序列。
具体而言,就是对于给定的n个数据,首先将每相邻的长度为1的子序列进行归并,得到n/2个长度为1或2的有序子序列,再将其两两归并,直到得到一个有序序列为止。
例如:对于数组{49,38,65,97,76,13,27}为例,排序过程如下:
第一步:[49][38] [65][97] [76][13] [27]
第二步:[38,49] [65,97] [13,76] [27]
第三步:[38,49,65,97] [13,27,76]
第四步:[13,27,38,49,65,76,97]
二路归并排序的过程需要进行logn次,每次归并的时间复杂度为O(n),所以总的时间复杂度为O(nlogn)
递归:
//归并排序
#include<stdio.h>
//归并
void Merge(int a[], int p, int q, int r)
{
int i, j, k, n1, n2;
n1 = q - p + 1;
n2 = r - q;
//给整型指针L,R分配内存空间
int* L = new int[n1];
int* R = new int[n2];
//将数组的值分别放入L和R中
for (i = 0, k = p;i < n1;++i, ++k)
L[i] = a[k];
for (i = 0, k = q + 1;i < n2;++i, ++k)
R[i] = a[k];
//两数组从大到小比较,直到一个数组遍历完为止
for (k = p, i = 0, j = 0;i < n1&&j < n2;++k)
{
if (L[i] < R[j])
{
a[k] = L[i];
++i;
}
else
{
a[k] = R[j];
++j;
}
}
//如果L还没排序完,则把剩下的数依次放在a[k]后
if (i < n1)
{
for (;i < n1;++i, ++k)
a[k] = L[i];
}
//如果R还没排序完,则把剩下的数依次放在a[k]后
if (j < n2)
{
for (;j < n2;++j, ++k)
a[k] = R[j];
}
}
//归并排序
void MergeSort(int a[], int p, int r)
{
//当p<r时执行,直到两数组个数均为1时
if (p < r)
{
int q = (p + r) / 2;
MergeSort(a, p, q);
MergeSort(a, q + 1, r);
Merge(a, p, q, r);
}
}
int main()
{
int array[] = { 1,4,7,9,2,3,5,8 };
int len = sizeof(array) / sizeof(array[0]);
MergeSort(array, 0, len - 1);
for (int i = 0;i < len;++i)
printf("%d ", array[i]);
printf("\n");
return 0;
}
非递归:
#define LENGTH 10
#include <stdio.h>
#include <stdlib.h>
//归并
void Merge(int *S, int *T, int low, int mid, int high)
{
int j, k, l;
for (k = low,j = mid+1;low<=mid&&j<=high;++k)
{
if (S[low] < S[j])
T[k] = S[low++];
else
T[k] = S[j++];
}
if (low <= mid)
{
for (l = 0;l <= mid - low;++l)
T[k + l] = S[low+l];
}
if (j <= high)
{
for (l = 0;l <= high - j;l++)
T[k + l] = S[j + l];
}
}
void MergePass(int *S, int *T, int w, int n)
{
int s =w,l;
int i = 0;
while ((i + 2 * s - 1) < LENGTH)
{
Merge(S, T, i, i + s - 1, i + 2 * s - 1);
i += 2 * s;
}
//把i到i+s和i+s到LENRTH-1再归并
if (i + s - 1 < LENGTH)
{
Merge(S, T, i, i + s - 1, LENGTH - 1);
}
//如果不满足i+s-1<LENGTH则表示剩下的为有序,直接赋给数组即可
else
{
for (l = i;l < LENGTH;++l)
T[l] = S[l];
}
}
void MergeSort(int *a)
{
int *temp = (int*)malloc(sizeof(int)*LENGTH);
//k用来表示每次k个元素归并
int k = 1;
while (k < LENGTH)
{
//先借助辅助空间归并
MergePass(a, temp, k, LENGTH);
//k每次乘以2是遵循1->2->4->8->16的二路归并元素个数的原则
k *= 2;
//再从辅助空间将排过序的序列归并回原数组
MergePass(temp, a, k, LENGTH);
k *= 2;
}
}
int main()
{
int i;
int array[LENGTH] = { 1,3,5,7,9,2,4,6,8,10 };
printf("Before Sorting:\n");
for (i = 0;i < LENGTH;++i)
printf("%d ", array[i]);
printf("\n");
MergeSort(array);
printf("After Sorting:\n");
for (i = 0;i < LENGTH;++i)
printf("%d ", array[i]);
printf("\n");
return 0;
}
非递归的方法,避免了递归时深度为log2(n)的栈空间,空间只是申请归并临时用到的temp数组,因此空间复杂度O(n),并且避免递归也在时间性能上有一定的提升。