python 归并排序

归并排序仍然是利用完全二叉树实现,它是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列。

基本过程:假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2个长度为2或1的有序子序列,再两两归并,最终得到一个长度为n的有序序列为止,这称为2路归并排序。

下面的截图来自《大话数据结构》相关章节,便于理解整个代码的实现过程。

                               

图中主要表明了实例代码中的两部分,分别为原始序列的拆分和合并两部分。

下面是实例代码:

# -*- coding:utf-8 -*-
__author__ = 'webber'
import random, time

def merge_sort(lst):
    if len(lst) <= 1:
        return lst          # 从递归中返回长度为1的序列

    middle = len(lst) / 2
    left = merge_sort(lst[:middle])     # 通过不断递归,将原始序列拆分成n个小序列
    right = merge_sort(lst[middle:])
    return merge(left, right)

def merge(left, right):
    i, j = 0, 0
    result = []
    while i < len(left) and j < len(right):  # 比较传入的两个子序列,对两个子序列进行排序
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
    result.extend(left[i:])         # 将排好序的子序列合并
    result.extend(right[j:])
    return result

if __name__ == "__main__":
    start = time.clock()

    rand_lst = []
    for i in range(6):
        rand_lst.append(round(random.random()*100, 2))
    lst = merge_sort(rand_lst)

    end = time.clock()
    print lst
    print "done  ", (end-start)

 

性能方面:

由于和堆排序类似,都是利用完全二叉树的相关性质,所以它在时间复杂度方面,最好、最坏和平均的时间复杂度都是O(nlogn);但是,在空间复杂度方面,由于它开辟了一块新的内存空间用来存放left和right子序列,而两个子序列的总大小其实和lst是一样的,为n,所以它的空间复杂度为O(n),这是归并排序的主要弱点,牺牲了空间复杂度来换取时间复杂度的减少。稳定性方面,只要在关键码相同时采用左序列元素先行的原则,就能保证算法的稳定性,另一方面,归并排序算法没有适应性,无论对于什么样的序列它都要做logn遍的递归。在《大话数据结构》一书中,对于归并排序,作者提倡尽量考虑非递归的方法(在c中)。

 

这里记录一下,python有一个模块,专门提供了归并排序的方法,叫做“heapq”模块,因此我们只要将分解后的结果导入该方法即可。例如:

from heapq import merge

def merge_sort(lst):
    if len(lst) <= 1:
        return lst          # 从递归中返回长度为1的序列
    middle = len(lst) / 2
    left = merge_sort(lst[:middle])     # 通过不断递归,将原始序列拆分成n个小序列
    right = merge_sort(lst[middle:])
    return list(merge(left, right))

 

posted @ 2016-12-07 20:40  webber_liu  阅读(5015)  评论(0编辑  收藏  举报