归并排序使用的是名叫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)。从渐进的角度来说,归并排序比之前的插入排序或者选择排序都快的多,但是由于它并不是原地排序,因此运行时间函数中的常数因子较大,当输入规模较小时,效率并不如之前的两种排序。

posted on 2012-01-06 00:23  LeavingQ  阅读(336)  评论(0)    收藏  举报