归并排序

以下来自百度百科

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

基于比较式的排序算法(Comparison based sorting algorithm,C.B.A)的时间复杂度可以用这样一个式子表示:Ω(nlogn)-> T(n) -> O(n ^ 2),也就是说它是以nlogn为下界,以n ^ 2为上界的这样一个表达式。经典的冒泡排序(BUBBLE-SORT)无论怎么改进,它的最坏情况还是O(n ^ 2)的时间。那我们能不能期望有一种排序算法,即使是最坏的情况它也只用O(nlogn)的时间来完成排序,答案就是归并排序,下面将给出相关证明。

理解归并算法原理的朋友可以很清楚的知道它的时间复杂度 = 递归分解时间 + 子序列合并时间,递归分解就是将一个规模为n的问题转换成了两个规模为n / 2的问题,因此递归分解的总时间就是2T(n / 2),最终子序列合并时间时间为O(n),所以最终用时为T(n) = 2T(n / 2) + O(n)。这只是一个递推式子,还不能得到最终的显式表达,它主要是表征如下代码的时间复杂度:

//时间复杂度的估算是渐进意义上的结果,随着计算规模的不断增加,一些常数项、低次项、倍数项的省略不会影响最终的上界,如下三个函数的用时就已经足够说明归并排序的性能。
mergeSort(source, tmp, startIdx, midIdx);
mergeSort(source, tmp, midIdx + 1, endIdx);

merge(source, tmp, startIdx, midIdx, endIdx);

显然有上述这样的一个递推表达还不能证明归并排序的时间复杂度,我们还需要做出其他努力。参考这样一张图:

这里写图片描述

这是上述递归式子的图示,假设最终递归出的元问题只需要常数的时间O(1),这是毋庸置疑的,因为到最终排序时是对于两个数的排序。那么可以看出这棵二叉树的每一层就需要O(n)的时间,而完全二叉树的深度(这里就是层数)可以表示为对n求以2为底的对数再加一(ps:这里不易以数学符号表达,同时笔者找了一些对这个公式的证明,因牵连太多关于二叉树的性质,笔者也还没接触二叉树,这里就不罗列出证明过程了。),所以有:T(n) = O(nlogn)

算法的具体实现想必各位不难找到,之后你就会理解归并排序的空间复杂度是O(n)的原因。笔者也看了一些实现代码,个人觉得传递一个规模为n的临时向量来代表空间的消耗,并以此协助排序的完成,会优于每次递归排序时临时申请具体更短的向量长度。当然这只是站在时间的角度上考虑,但如果你都选择了这样一个排序算法,说明空间上消耗肯定是你所能承受的。

这个算法要体现分而治之的思想,就需要通过首尾位置来找到每次递归时的分治的标杆值。通常的做法是首尾相加除二,但在某些代码里看到的是右移一位。这让笔者好奇,百度了一番,找到了一些答案。

所谓的乘除法其实就是通过多次的加法和减法完成的,这就意味着要花费更多的时间来完成,同时移位运算的所占的机器周期要短与乘除指令。当涉及到大数据运算时,这一细微的性能差异就会被逐渐发大,最终让你感觉到明显的时间差,所以有关2的倍数的乘除最好采用移位操作。

参考:

邓俊辉:数据结构与算法教学视频

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值