排序-归并排序

倖福魔咒の 提交于 2020-11-28 03:40:21

零、数据结构和算法系列目录

数据结构和算法系列目录(不断更新):

http://my.oschina.net/liuzeli/blog/128278

一、归并排序简介

归并排序和插入排序一样,其核心是基于比较的,所以也是比较排序中的一种。归并排序运用的分治算法的思想。在介绍归并排序的同时也简单的介绍一下分治算法,这种算法的核心讲解会在后续的博客中介绍。回国头来继续说归并排序。归并排序有着很好的应用场景,它适合给大规模的数据排序,当数据量非常大时,内存排序困难可以对归并排序稍加改动让其支持分批从硬盘读取数据并进行排序,最后把结果进行合并。归并排序(二路归并)的主要思路就是假设两个数组A和数组B分别已经是排序好的,再对这两个数组A和B进行合并成为一个新的排序好的数组。这一步是归并排序的核心步骤,如果对于这个待排序数组来说,开始先对数组两个两个元素进行排序,在对其合并变成4个元素为一组,在变成8个元素为一组,从而到最后完成所有数据的排序。

二、分治算法的简介

分治算法的主要核心是分而治之的思想,即把原文题进行分解,分解成若干的子问题,在对子问题进行求解,在对所有子问题的答案进行合并从而得到原问题的结果。可以表示成下面叙述的过程。
1. 分解:将原问题分解成若干个规模较小且相对独立的的与原问题形式相同的子问题。
2. 求解:若子问题规模较小并且容易解决时将对其进行求解,否则递归的解决子问题。
3. 合并:将各个子问题的解进行合并,从而得到原问题的解。
对于分治算法会在后续的博客中继续讲解,这里只要有一个初步的印象即可。

三、归并算法的步骤

其实归并算法的步骤完全可以用上面分支算法的求解过程来套用。
1. 分解:将n个元素分成各含有n / 2个元素的子序列。
2. 求解:用合并算法对两个子序列递归地排序。

3. 合并:合并两个已排序的子序列得到排序结果。

四、合并子序列算法的步骤 

从上面归并排序的步骤中可以看出,其核心算法是对两个排序好的子序列进行合并。由于这个过程需要检测数组A和数组B的剩余元素是否为空,所以这里可以增加一个哨兵来避免这个判断的过程,从而可以使排序更快的运行。我们在数组A和B中的最后各增加一个哨兵,把它取为待排序数组中一个达不到的很大的数。这样一来,我们就可以避免检验数组A和B是否为空的情况。具体步骤如下:
1. 对数组A和B进行初始化,将原数组的已排序好的两部分赋值给A和B,并在其最后加入哨兵元素。
2. 比较数组A中的元素A[i]和数组B的元素B[j],将两者最小的放入到原数组中相应的位置。
3. 执行第二步n次(n为数组A和数组B中元素的个数和(不包括哨兵元素))。

五、归并排序的分析 

先对合并子序列进行分析。对两个已经排序好的数组A和B,分别含有n1和n2个元素,容易得出对于这个合并的时间复杂度为O(n1+n2)。另外,由于要在原数组中进行原地排序所以还需要O(n1+n2)个辅助空间的空间复杂度。从整体上来说的话,去n1+n2为n(原待排序数组的个数),即可得出合并子序列的时间和空间复杂度均为O(n)。再对归并算法进行分析。每次将原数组分成两半,很容易的得出需要分成lgn。我们进一步可以得出整个归并排序的时间复杂度为O(nlgn),空间复杂度为O(n)。从排序的稳定性来说,归并排序是一个稳定的排序算法。

六、归并排序的例子

这里给出一个动态的图片来表示归并算法的实现过程(博客中可能看不见动态过程,请参考图片源地址,来自参考文献2):




七、代码实现

归并算法的实现:

int
merge_sort(int *numbers, int begin, int end)
{
	int merge(int *, int, int, int);
	int middle;

	if(begin < end)
	{
		middle = (end + begin) / 2;
		merge_sort(numbers, begin, middle);
		merge_sort(numbers, middle + 1, end);
		merge(numbers, begin, middle, end);
	}

	return 0;
}
合并子序列算法的实现:
int
merge(int *numbers, int begin, int middle, int end)
{
	int n1 = middle - begin + 1;
	int n2 = end - middle;
	int* numbers1 = (int*)malloc((n1 + 1) * sizeof(int));
	int* numbers2 = (int*)malloc((n2 + 1) * sizeof(int));
	int i,
	    j,
	    k;

	for(i = 0; i < n1; i++)
		numbers1[i] = numbers[begin + i];
	numbers1[i] = MAX;
	for(i = 0; i < n2; i++)
		numbers2[i] = numbers[middle + i + 1];
	numbers2[i] = MAX;
	
	i = 0;
	j = 0;
	for(k = begin; k < end + 1; k++)
	{
		if(numbers1[i] < numbers2[j])
		{
			numbers[k] = numbers1[i];
			i++;
			continue;
		}
		else
		{
			numbers[k] = numbers2[j];
			j++;
			continue;
		}
	}

	free(numbers1);
	free(numbers2);

	return 0;
}

八、后续工作

归并排序是一个适用于大规模数据的排序算法,是一种非常常用的排序算法。后面还会陆续的介绍其他的算法。另外,归并算法也是一个应用分治算法思想的典型实例,后面将对分治算法进行进一步讲解。

九、参考资料

1. Introduction to Algorithms(Second Edition), Thomas H.Cormen & Charles E.Leiserson
2. Merge Sort, http://en.wikipedia.org/wiki/Merge_sort
3. Divide and conquer algorithm, http://en.wikipedia.org/wiki/Divide_and_conquer_algorithm


说明:

数据结构和算法博客系列的目录为:http://my.oschina.net/liuzeli/blog/128278

如有错误还请各位指正,欢迎大家一起讨论给出指导。

上述程序完整代码下载链接:

https://github.com/zeliliu/BlogPrograms/tree/master/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%26%E7%AE%97%E6%B3%95/sort/merge%20sort

最后更新时间2013-07-07

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!