4. 归并排序

一、基本思想

对于一个待排序的序列,递归地将前半部分数据和后半部分数据各自归并排序,得到排序后的两部分数据,然后合并这两个部分。

归并算法采用分而治之的策略:

       a. 将问题成一些小的问题然后递归求解;

  b. 将分的阶段解得的各个答案“修补”到一起。

可以看到这种结构很像一棵完全二叉树,故我们可以采用递归来实现归并排序。

二、分阶段可以理解为就是递归拆分子序列的过程

由上图可知递归深度为logn,故其时间复杂度为O(logn)。

三、治阶段可以理解为就是合并相邻有序子序列的过程

我们需要将两个已经有序的子序列合并成一个有序序列。因为这两个输入子序列是有序的,所以若将输出放到第三个序列中时,则该算法可以通过对输入数据一趟排序来完成。即合并两个有序子序列的时间复杂度为O(n)。

比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],下图为对应的实现步骤。

四、代码

/* 归并排序 */
void MergeSort(int a[], int n)
{
	//在排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间
	int *temp = new int[n];
	sort(a, 0, n-1, temp);
	delete []temp;
} 

void sort(int a[], int left, int right, int temp[])
{
	int mid;
	if(left < right) {
		mid = (left + right) / 2;
		sort(a, 0, mid, temp);				// 左边归并排序,使得左子序列有序 
		sort(a, mid + 1, right, temp);		// 右边归并排序,使得右子序列有序 
		merge(a, left, mid + 1, right, temp);		// 将两个有序子数组合并 
	}
}

void merge(int a[], int lPos, int rPos, int rightEnd, int temp[])
{
	int leftEnd = rPos - 1;						// 左边子序列的右端 
	int tempPos = lPos;							// 临时数组的游标 
	int elementNum = rightEnd - lPos + 1;		// 两个子序列拥有的总元素 
	
	// 合并两个子序列 
	while(lPos <= leftEnd && rPos <= rightEnd) {
		if(a[lPos] <= a[rPos])
			temp[tempPos++] = a[lPos++];
		else
			temp[tempPos++] = a[rPos++];
	}
	while(lPos <= leftEnd)
		temp[tempPos++] = a[lPos++];
	while(rPos <= rightEnd)
		temp[tempPos++] = a[rPos++];
	// 拷贝到数组a中	
	for(int i = 0; i < elementNum; ++i, --rightEnd)
		a[rightEnd] = temp[rightEnd];
}

  

原文地址:https://www.cnblogs.com/xzxl/p/9581540.html