归并排序使用的是名叫DIVIDE AND CONQUER的分治策略。将一个规模为n的问题分解为a个规模为n/b的子问题,然后递归地CONQUER它们,最后将各个子问题的解COMBINE就可以得到原来规模为n的原始问题的解。归并排序是递归地处理两个规模为floor(n/2)和ceil(n/2)的数组,然后再把它们合并起来。
说实话递归一直是让我头疼的问题,脑子总是转不过弯来。对于归并排序来说,递归的过程比较简单,只是不断的调用自己处理规模不断变小的子问题,直到达到递归终止条件。在这里,终止条件是序列只有一个元素,它意味着这个序列是已经排序好的,然后再对2个已经排好序的数列做合并操作。
比较好的理解方式是从递归树的底部看待归并函数,我们对一个序列递归地调用函数,直到底部得到一个长度为2的序列,此时再调用函数,则分别得到2个已排好序的单元素序列,这时递归过程终止,开始回代过程,调用合并函数将2个已排序的序列合并成一个新的已排序的序列。类似的过程在递归树的每一层发生,具体的顺序依赖于树的遍历方式,是先处理左半序列还是右半序列。
合并子序列:
这个过程的思路非常直观,每次循环比较两个已排序子序列最左边的元素,也就是当前序列中最小的元素,将较小的元素加入道新序列尾部。这个操作的时间复杂度很容易看出来是Θ(n),因为无非是遍历2个数组做一些比较和传送。
代码如下
from string import *
def mergeSort(L):
if len(L) < 2:
return L
else:
mid = len(L)/2
left = mergeSort(L[:mid])
right = mergeSort(L[mid:])
F = merge(left, right)
return F
def merge(left, right):
F = []
i = j = 0
while i < len(left) and j < len(right):
if left[i] < right[j]:
F.append(left[i])
i = i + 1
else:
F.append(right[j])
j = j + 1
if i < len(left):
F = F + left[i:]
if j < len(right):
F = F + right[j:]
return F
Merge Sort的运行时间可以用递归式来表示:
T(n) = 2T(n/2) + cn
cn代表合并2个规模为n/2的子序列的代价为n的常数倍。
用递归树或者Master Method可以求解这个递归式,答案T(n) = Θ(nlgn)。从渐进的角度来说,归并排序比之前的插入排序或者选择排序都快的多,但是由于它并不是原地排序,因此运行时间函数中的常数因子较大,当输入规模较小时,效率并不如之前的两种排序。
浙公网安备 33010602011771号