归并排序

归并排序是分治思想的一个应用,其将原问题分解成规模比较小的问题求解,然后再组合成原问题的解。在讲解归并排序之前,先要编写一个将两个有序数组合并成一个有序数组的程序。

int* merge(int *a1, int *a2, int n1, int n2)
{
	int *result = (int *)malloc(sizeof(int)*(n1 + n2));
	int *temp = result;
	int i = 0, j = 0;
	while (i < n1 && j < n2)
	{
		if (*a1 < *a2)
		{
			*temp++ = *a1++;
			i++;
		}
		else
		{
			*temp++ = *a2++;
			j++;
		}

	}
	while (i < n1)
	{
		*temp++ = *a1++; 
		i++;
	}
	while (j < n2)
	{
		*temp++ = *a2++;
		j++;
	}
	return result;
}
上述程序将有序数组a1和a2合并成一个新的有序数组,其中n1和n2分别为a1和a2的大小。合并的方法很简单,依次遍历数组a1和a2,将小的元素插入到新数组对应位置中即可。因为a1和a2本身就是有序的,所以合并之后的数组也是有序的。

例如a1={1,2,3,4,5},a2={2,3,5,6,7,8,9},i和j分别指向a1和a2的当前元素,初始化为i=0,j=0,合并的新数组为result,那么合并的流程如下:

result={1},i=1,j=0

result={1,2},i=2,j=0

result={1,2,2},i=2,j=1

result={1,2,2,3},i=3,j=1

result={1,2,2,3,3},i=3,j=2

......

result={1,2,2,3,3,4,5,5,6},i=5,j=4

result={1,2,2,3,3,4,5,5,6,7},i=5,j=5

......

result={1,2,2,3,3,4,5,5,6,7,8,9}

特别的,针对上述合并过程,对于只有一个元素的数组,合并过程就是简单的比较过程,因为只有一个元素的数组肯定是有序的。


归并排序就是基于上述合并过程工作的。归并排序将要排序的数组划对半分成两个数组,然后对这两个数组调用同样的归并过程,最后将它们合并。

假设有一个8元素数组,其归并排序的过程如下图的归并树所示。

其实归并排序是一个自底向上的过程,例如上图中的8元素数组,其先将数组划分成单元素,然后相邻两个元素进行合并,得到4个新的有序数组,然后继续相邻两个数组进行合并,得到两个新的有序数组,最后再合并得到数组排序后的结果。上图的归并树的每一层的合并过程,不管是多少组数组进行两两合并,其过程也只是遍历一次原数组的全部元素而已,故其时间复杂度为O(n),而树的高度为lg(n),所以归并排序的时间复杂度为O(nlg(n))。

void mergeSort(int *arr, int n)
{
	if (n <= 1)
	{
		return;
	}
	mergeSort(arr, n / 2);
	mergeSort(arr + n / 2, n - n / 2);
	int *result = merge(arr, arr + n / 2, n / 2, n - n / 2);
	for (int i = 0; i < n; i++)
	{
		*arr++ = *(result+i);
	}
	free(result);
}
由于归并排序是一种变址排序,所以上述代码在归并完成后需要对原数组重新赋值。其实如果没有必要,也可以不这样做,直接返回排序后的数组即可。

你可能感兴趣的:(归并排序)