1. 程式人生 > >Python裡sort()的排序演算法--Timsort簡介

Python裡sort()的排序演算法--Timsort簡介

      學習計算機的肯定對各種排序演算法都很瞭解,這裡說一下,常用的排序演算法有氣泡排序插入排序,快速排序等,

      而Python裡的sort排序是一種名為Timsort的排序方法,其時間複雜度為O(n log n),而且這是一種快速的穩定的排序方法。它的發明者是Tim Peters在2001年為Python創造的一種排序演算法。下圖是Timsort的時間複雜度的介紹,可以看到Timsort排序在各方面都是最優的。而且Timsort是在C語言中實現的,因此Timsort排序的效能是毋庸置疑的。

       一個演算法的穩定

主要是:在假定在待排序的記錄序列中,存在多個具有相同的關鍵字的記錄,若經過排序,這些記錄的相對次序保持不變,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序後的序列中,r[i]仍在r[j]之前,則稱這種排序演算法是穩定的;否則稱為不穩定的。通俗的來說,就是兩個相同的值,在排序後位置不發生交換,就是穩定的,否則就不穩定。在一些條件下,穩定和不穩定也是相對的。

    Timsort在排序長度低於64的時候採取:插入排序 。高於64的時候採取Timsort是一種改良的歸併排序。下圖是插入排序的時候的演算法:

正常的歸併排序如下:

如 設有數列{6,202,100,301,38,8,1}

初始狀態:6,202,100,301,38,8,1

第一次歸併後:{6,202},{100,301},{8,38},{1},比較次數:3;

第二次歸併後:{6,100,202,301},{1,8,38},比較次數:4;

第三次歸併後:{1,6,8,38,100,202,301},比較次數:4;

總的比較次數為:3+4+4=11;

逆序數為14;

而Timsort的演算法首先遍歷列表,查詢升序和降序的部分(Run),由於現實中的很多資料都是排好序的,Timsort利用了這一特點。Timsort排序的輸入的單位不是一個個單獨的數字,而是一個個的分割槽。其中每一個分割槽叫一個“run“(圖1)。針對這個 run 序列,每次拿一個 run 出來進行歸併。每次歸併會將兩個 run 合併成一個 run。每個run最少要有2個元素。Timesor按照升序和降序劃分出各個run:run如果是是升序的

,那麼run中的後一元素要大於或等於前一元素(a[lo] <= a[lo + 1] <= a[lo + 2] <= ...);如果run是嚴格降序的,即run中的前一元素大於後一元素(a[lo] >  a[lo + 1] >  a[lo + 2] >  ...),需要將run 中的元素翻轉(這裡注意降序的部分必須是“嚴格”降序才能進行翻轉。因為 TimSort 的一個重要目標是保持穩定性stability。如果在 >= 的情況下進行翻轉這個演算法就不再是 stable)。

如果降序,則翻轉序列:

 劃分run和優化run長度以後,然後就是對各個run進行合併。合併run的原則是 run合併的技術要保證有最高的效率。當Timsort演算法找到一個run時,會將該run在陣列中的起始位置和run的長度放入棧中,然後根據先前放入棧中的run決定是否該合併run。Timsort不會合並在棧中不連續的run。

Timsort會合並在棧中2個連續的run。X、Y、Z代表棧最上方的3個run的長度(圖2),當同時不滿足下面2個條件是,X、Y這兩個run會被合併,直到同時滿足下面2個條件,則合併結束:

(1) X>Y+Z

(2) Y>Z

例如:如果X<Y+Z,那麼X+Y合併為一個新的run,然後入棧。重複上述步驟,直到同時滿足上述2個條件。當合並結束後,Timsort會繼續找下一run,然後找到以後入棧,重複上述步驟,及每次run入棧都會檢查是否需要合併2個run。

 

合併run步驟

合併2個相鄰的run需要臨時儲存空閒,臨時儲存空間的大小是2個run中較小的run的大小。Timsort演算法先將較小的run複製到這個臨時儲存空間,然後用原先儲存這2個run的空間來儲存合併後的run(圖3)。

 

 

          臨時儲存空間,讓Timsort排序的空間複雜度為o(n)
          簡單的合併演算法是用簡單插入演算法,依次從左到右或從右到左比較,然後合併2個run。為了提高效率,Timsort用二分插入演算法(binary merge sort)。先用二分查詢演算法/折半查詢演算法(binary search)找到插入的位置,然後在插入。
         例如,我們要將A和B這2個run 合併,且A是較小的run。因為A和B已經分別是排好序的,二分查詢會找到B的第一個元素在A中何處插入(圖4)。同樣,A的最後一個元素找到在B的何處插入,找到以後,B在這個元素之後的元素就不需要比較了(圖5)。這種查詢可能在隨機數中效率不會很高,但是在其他情況下有很高的效率。

 

 run合併過程1

 

 

                                                                         圖5 run合併過程2