归并排序仍然是利用完全二叉树实现,它是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(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))