归并排序(merge sort)

9 篇文章 0 订阅
5 篇文章 0 订阅

归并排序时间复杂度为O(n*logn),需要和待排序列相同大小的辅助空间,是一种稳定排序

非递归实现的归并排序效率要高得多

归并排序属于分治法的一种,分治法定义为:将原问题划分为n个规模较小而结构与原问题相似的子问题,递归解决这些子问题,然后合并结果,得出原问题的解。因此分治法有以下三个步骤:

  • 分解:将原问题分解为一系列结构相似的子问题
  • 递归求解:对子问题递归求解
  • 合并(非必须,如二分查找、求乱序序列第K大的数均没有合并步骤):将子问题的解合并,得出原问题的解

归并排序的步骤为:

  • 将待排序序列自中心点分割为两个子序列,递归上述步骤直至子序列长度为1不能再分割
  • 自递归的最底层反向向上进行归并(merge)操作,具体为将两个各自有序的序列S1和S2合并成一个有序的新序列S3。递归最顶端的归并操作完成后,整个排序过程结束

 

代码(升序):

void merge(int a[], int left, int mid, int right){
	assert(a != NULL);
	assert(left >= 0);
	assert(mid >= left);
	assert(right >= mid);

	int len1 = mid-left+1;
	int len2 = right-mid;
	int * b1 = (int *)malloc((len1+1)*sizeof(int));  //需要额外辅助空间
	int * b2 = (int *)malloc((len2+1)*sizeof(int));
	memcpy(b1, a+left, len1*sizeof(int));  
	memcpy(b2, a+mid+1, len2*sizeof(int));
	b1[len1] = INT_MAX;  //哨兵
	b2[len2] = INT_MAX;

	int p = 0, q = 0;
	for(int i = left; i <= right; i++){
		if(b1[p] <= b2[q]) a[i] = b1[p++];  //为保证排序的稳定性,这里必须是<=而不是<
		else a[i] = b2[q++];
	}
	free(b1);
	free(b2);
}

void merge_sort(int a[], int left, int right){
	assert(a != NULL);
	assert(left >= 0);
	assert(right >= left);	

	if(left < right){
		int mid = left+(right-left)/2;
		merge_sort(a, left, mid);
		merge_sort(a, mid+1, right);
		merge(a, left, mid, right);
	}
}


Tips:

  • 分割序列时,由于整数除法的特性,应将序列分为子序列(low, mid)和(mid+1, high),而不是(low, mid-1)和(mid, high),因为若按后一种分法,当序列只含两个元素时会出问题。设当前序列为(n, n+1),low=n, high=n+1,故mid=(low+high)/2=(n+n+1)/2=n(C的整数除法特性)。此时按后一种分法,子序列分别为(n, n-1)和(n, n+1),这明显是错误的
  • 在归并函数内设置哨兵变量能够简化操作,不需要每次判断所归并的子序列是否为空。哨兵变量的置应比可能取的值都大,假设为∞,上面代码内设置为INT_MAX,这是C标准库定义的表示int类型最大值的常量,定义在<limits.h>中
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值