歸並排序仍然是利用完全二叉樹實現,它是建立在歸並操作上的一種有效的排序算法,該算法是采用分治法(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))