1. 程式人生 > >資料結構與演算法學習筆記之 適合大規模的資料排序

資料結構與演算法學習筆記之 適合大規模的資料排序

前言

  在資料排序的演算法中,不同資料規模應當使用合適的排序演算法才能達到最好的效果,如小規模的資料排序,可以使用氣泡排序、插入排序,選擇排序,他們的時間複雜度都為O(n2),大規模的資料排序就可以使用歸併排序和快速排序,時間複雜度為O(nlogn)。今天我們就來看一下歸併排序和快速排序

正文

  歸併排序的原理

  核心思想(分治思想):

    排序陣列,將陣列從中間分成前後兩部分,對前後兩部分分別排序,然後合在一起,這個陣列就是有序的。

  歸併排序的效能分析

  1.歸併排序是一個穩定的排序演算法:在合併的過程中,如果A[p...q]和A[q+1...r]之間中有相同的元素,先把A[p...q]中的元素放入tmp陣列。這樣就保證了值相同的元素,在合併前後的先後順序不變。

  2.歸併排序的時間複雜度是O(nlogn):在解決遞迴問題時,我們得出一個結論:遞迴問題可以寫成遞推公式,遞迴程式碼的時間複雜度也可以寫成遞推公式

  我們假設對n個元素進行歸併排序需要的時間是T(n),那分解成兩個子陣列排序的時間都是T(n/2),套用結論可以得到歸併排序的時間複雜度的計算公式就是:

T(1) = C;   n=1 時,只需要常量級的執行時間,所以表示為 C。
T(n) = 2*T(n/2) + n; n>1

再次將這個公式分解:

T(n) = 2*T(n/2) + n
     = 2*(2*T(n/4) + n/2) + n = 4*T(n/4) + 2*n
     
= 4*(2*T(n/8) + n/4) + 2*n = 8*T(n/8) + 3*n = 8*(2*T(n/16) + n/8) + 3*n = 16*T(n/16) + 4*n ...... = 2^k * T(n/2^k) + k * n ......

我們可以得到T(n)=2^kT(n/2^k)+kn.當T(n/2^k)=T(1)時,也就是n/2^k=1,我們將得到k=log2n,問你將k帶入公式得到

T(n)=Cn+nlog2n

用大O標記法來表示為T(n) 就等於 O(nlogn)

而且時間複雜度是非常穩定的:最好情況,最壞情況,還是平均情況,時間複雜度都是O(nlogn)

  3、歸併排序的空間複雜度為O(n)

  歸併排序的致命缺點:歸併排序不是原地排序演算法(在合併兩個有序陣列時,需要藉助額外的儲存空間)

遞迴程式碼的空間複雜度並不能像時間複雜度那樣累加、儘管每次合併操作都需要申請額外的記憶體空間,但在合併完成之後、臨時開闢的記憶體空間就被釋放掉了、臨時記憶體空間最大也不會超過 n 個數據的大小

  快速排序的原理

   如果要排序陣列中下標從p到r之間的一組資料,我們選擇p到r之間的任意一個數據作為pivot(分割槽點),遍歷資料,見小於pivot的放在右邊,大於pivot放在左邊。這樣陣列就分成了三部分,用遞迴排序下標從 p 到 q-1 之間的資料和下標從 q+1.到r之間的資料,直到區間縮小為1,說明資料都有序
  快速排序的時間複雜度為O(1):在排序過程中,假如遇到需要移動資料的,我們可以之間用交換的思想

(圖片來源於網路,侵刪)

  空間複雜度為O(1)

快速排序和歸併排序的區別?

看圖:

(圖片來源於網路,侵刪)

處理過程的差異:

  遞迴排序:先處理子問題再合併

  快速排序:先分割槽,再處理子問題

歸併排序雖然穩定,是時間複雜度為O(nlogn)的排序演算法,但是它不是原地排序演算法,合併過程中需要額外的空間。

快速排序的效能分析

  遞迴程式碼的時間複雜度,如果每次分割槽操作,都能正好將陣列分為兩個大小相等的兩個小區間,那快速排序的遞推公式和遞推排序是相同的,所以,快排的時間複雜度為O(nlogn)

  但是,每次都分得那麼均勻是非常難實現的。

T(n)在大部分情況下的時間複雜度都可以做到O(nlogn),只有在極端情況下才會退化為O(n2).

後記

  遞迴和快排都是分治的思想,程式碼都通過遞迴來實現,過程非常相似。歸併排序時間複雜度都非常穩定為O(nlogn),但是每次合併的時候都需要額外的空間,空間複雜度非常高為是O(n),快速排序演算法雖然最壞時間複雜度為O(n2),但是平均時間複雜度為O(nlogn),最壞的情況我們也可以避免。

相關文章

以上內容為個人的學習筆記,僅作為學習交流之用。

 

歡迎大家關注公眾號,不定時乾貨,只做有價值的輸出