1. 归并排序与分治策略
归并排序的核心思想就是 分而治之。
先介绍下分治法,设计思想是:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
分治策略:对于一个规模为N(N较大)的问题,将其划分为K个规模较小的子问题,若子问题相互独立且与原问题形式相同,我们则可以使用递归不断地将子问题进行划分,将一个大问题自顶向下一层层划分为容易解得小问题,得到小问题的解后再自底向上逐层合并得到原问题的解。
归并排序就是采用了这样的分治策略来设计算法。对于一个长度为N的数组,通过逐层二分将大数组分解成容易排序的小数组,然后对排好序的两个子数组进行归并,最终得到归并后的有序数组。
使用增量策略的排序(如插入排序)的算法复杂度为O(n^2),而使用分治策略的归并排序则将算法复杂度降到了O(nlogn)。经过测试,这种排序算法的优化效果是很显著的,大家可以在最后看到测试时各种方法所用时间的对比。
2. 自顶向下、自底向上、使用插入排序的归并算法实现
(1)归并算法
有了分治的策略,那么我们如何将排好序的子数组合并成一个有序的数组呢?这就是归并排序的核心算法merge(a,lo,mid,hi)。
对于上图看了代码应该很容易理解:
需要一个辅助数组aux,现将a的元素拷贝给aux,再将aux元素归并到a中。
def __merge(self,a,lo,mid,hi):
i=lo
j=mid+1
self.__aux[lo:hi+1]=a[lo:hi+1] #将a[lo..hi] 复制给aux[lo..hi]
for k in range(lo,hi+1):
if i > mid:#左侧元素归并完毕,将右侧剩余元素依次拷贝到a
a[k] = self.__aux[j]
j+=1
elif j > hi:#右侧,同上
a[k] = self.__aux[i]
i+=1
elif self.less(self.__aux[j],self.__aux[i]):#谁小将谁归并入a
a[k] = self.__aux[j]
j+=1
else:
a[k] = self.__aux[i]
i+=1
(2)自顶向下的归并排序
有了以上的归并操作后,我们如何递归地划分数组并将两个子数组排序呢?通过递归直到将子数组划分为长度为1的数组,此时hi <= lo开始return,归并后的数组即是长度为2,4,8,…,n的有序数组。
def __sort_by_recurse(self,a,lo,hi): # 使用递归进行自顶向下归并
if hi <= lo:
return
mid=int((hi+lo)/2)
self.__sort_by_recurse(a,lo,mid)
self.__sort_by_recurse(a,mid+