排序算法——归并排序

       归并排序是多次将两个或两个以上的有序表合并为一个新的有序表。最简单的归并是二路归并,即将两个有序表合并为一个有序表。二路归并排序的基本思想是:将R[0..n-1]看作是n个长度为1的有序子表,然后进行两两二路归并,得到[n/2](对n/2取整数)个长度为2(最后一个子表的长度可能小于2)的有序子表;再次进行两两二路归并,得到[n/4]个长度为4(最后一个子表的长度可能小于4)的有序子表,...,直到得到一个长度为n的有序表。

/**
* 归并排序
*
* 算法:归并排序(Merge Sort)
* 输入:待排序元素的数组,待排序元素个数
* 输出:
* 原理:将待排序的表R[0..n-1]看做是n个长度为1的有序子表,将这些子表两两归并;将第一趟归并得到的子表继续两两归并;重复上述操作,直到最后得到一个长度为n的有序表
* 过程:
*  1)将待排序的表R[0..n-1]看做是n个长度为1的有序子表,将这些子表两两归并,得到的子表的长度为2(最后一个子表的长度可能不是2)
*  2)将第一趟归并得到的所有子表继续两两归并,得到的子表的长度为4(最后一个子表的长度可能不是4)
*  3) ...
*  4)重复上述操作,直到得到长度为n的有序表
*  5) ...
*  6)最终得到的R[0..n-1]中的元素递增排序
*
* 时间复杂度为O(nlog2(n)),空间复杂度为O(n),稳定的排序方法
*/
void mergeSort(RecType R[], int n)
{
	//自底向上的二路归并算法
	int length;
	//进行log2(n)趟归并
	for (length = 1; length < n; length = 2 * length)
		mergePass(R, length, n);
}

/**
 * 目的:对整个表进行一趟归并
 * 作用:将R[0..n-1]中长度为length的有序子表,合并为长度为2length的有序子表
 * 原理:在某趟归并中,设各子表长度为length(最后一个子表的长度可能小于length),则归并前R[0..n-1]中共有[n/length]个有序子表:R[0..length-1],R[length..2length-1],...,R[[n/length]*length..n-1]。其中,[n/length]表示取整数
		 调用Merge()函数将相邻的一对子表进行归并。
 * 特殊情况:若子表个数为奇数,则最后一个子表无序归并;若子表个数为偶数,则要注意最后一个子表的区间上界是n-1,最后一个子表的长度可能小于length
 * 过程:
 *	1) 调用Merge()函数将所有的长度为length的相邻的一对子表归并
 *	2) 判断剩余一个子表还是两个子表
 *	3)若还剩两个子表,则归并这两个子表;否则,不做任何操作
 *
 */
void mergePass(RecType R[], int length, int n)
{
	int i;
	//归并length长的两个相邻子表
	for (i = 0; i + 2 * length - 1 < n; i = i + 2 * length)
	{
		merge(R, i, i + length - 1, i + 2 * length - 1);
	}
	//余下两个子表,后者长度小于length
	if (i + length < n)
	{
		//归并这两个子表
		merge(R, i, i + length - 1, n - 1);
	}
}

/**
 * 目的:将两个有序表R[low..mid]和R[mid+1..high]直接归并为一个有序表
 * 原理:每次从两个有序表(递增)中取出一个元素进行关键字的比较,将较小者放入一个局部的暂存数组R1中,最后将各有序表中余下的部分直接复制到R1中,得到有序表R1
 * 过程:
 *	1) 初始化R1,用于保存合并后的有序表
 *	2) 分别从两个有序表(递增)中取出第一个元素,比较它们的关键字大小,将关键字较小的元素放入R1中,并将该元素从原来的有序表中删除
 *	3)继续从两个有序表中取出第一个元素进行比较,直至某一个有序表中没有任何元素可以取出来比较
 *	4) ...
 *	5) 将还有剩余元素的有序表中的元素全部放入R1中
 *	6) 得到的R1[0..high-low]中的元素递增排序
 *	7) 将R1中的元素复制回R中
 *	8) 最终得到的R[low..high]中的元素递增排序
 *
 */
void merge(RecType R[], int low, int mid, int high)
{
	RecType *R1;
	//k是R1的下标,i、j分别为两个有序表的下标
	int i = low, j = mid + 1, k = 0;
	//动态分配空间,长度为待合并的两个有序表的长度之和
	R1 = new RecType[high - low + 1];
	//R1=(RecType *)malloc((high-low+1)*sizeof(RecType));
	//循环第1个有序表和第2个有序表
	while (i <= mid && j <= high)
	{
		//取两个有序表的第一个元素进行比较
		if (R[i].key <= R[j].key) //将第1有序表中的元素放入R1中
		{
			R1[k] = R[i];
			i++;
			k++;
		}
		else //将第2有序表中的元素放入R1中
		{
			R1[k] = R[j];
			j++;
			k++;
		}
	}
	//将第1有序表余下部分复制到R1中
	while (i <= mid)
	{
		R1[k] = R[i];
		i++;
		k++;
	}
	//将第2有序表余下部分复制到R1中
	while (j <= high)
	{
		R1[k] = R[j];
		j++;
		k++;
	}
	//将R1复制回R中
	for (k = 0, i = low; i <= high; k++, i++)
		R[i] = R1[k];
	//释放R1的空间
	delete R1;
	//free(R1);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值