演算法-排序4(總結)
如何實現一個通用的,高效能的排序演算法。
總結一下前面學過的幾種排序演算法
演算法 | 時間複雜度 | 是穩定排序 ? | 是原地排序? |
---|---|---|---|
氣泡排序 | O(n^2) | ✔ | ✔ |
插入排序 | O(n^2) | ✔ | ✔ |
選擇排序 | O(n^2) | ✖ | ✔ |
快速排序 | O(n*logn) | ✖ | ✔ |
歸併排序 | O(n*logn) | ✔ | ✖ |
計數排序 | O(n+k) k是資料範圍 | ✔ | ✖ |
桶排序 | O(n) | ✔ | ✖ |
基數排序 | O(dn) d是維度 | ✔ | ✖ |
線性排序時間複雜度比較低,但是對資料的要求十分嚴格,所以不能用作通用排序的方案。
小資料規模的排序,首選時間複雜度為 O(n^2) 的演算法。如果資料規模較大,選擇時間複雜度為 O(nlogn) 的演算法。所以為了兼顧任意規模的資料排序,那就首選時間複雜度為 O(n
歸併排序和快速排序時間複雜度都為 O(n*logn),而且極端情況下快速排序的時間複雜度會變成 O(n^2),但是二者之間還是快速排序優先被選用。歸併排序不是原地排序,在排序期間會申請同樣資料大小的記憶體空間,所以空間複雜度上要高的多。
雖然優先選擇的是快速排序,但是極端情況下時間複雜度會退化為 O(n^2),那麼如何避免這種情況的發生呢?這就是接下來要總結的如何優化快速排序演算法。
優化快速排序演算法
當待排序資料呈現有序或者接近有序狀態,每次分割槽選擇的分割槽點都是最後一位的時候,快速排序演算法時間複雜度退化為 O(n^2)。那麼避免這種情況的發生就是每次選取的分割槽點,儘量將資料均分成兩份。下面介紹兩種選取分割槽點的方法。
方法一:三數取中法。分別獲取分割槽區間的第一個,中間位置,最後一個元素,比較大小,選擇中間的值作為分割槽點。如果分割槽區間的資料量過大,就可以採用五數取中,十數取中,儘量保證分割槽點的選擇趨於中間數值。
方法二:隨機法。隨機在分割槽區間選擇一個數據,根據概率來判斷,分割槽點選到最小值或者最大值得概率是很小的,所以時間複雜度退化為 O(n^2) 的概率也是很小的。
演算法實現舉例分析
王爭老師舉例了 Glibc 中 qsort() 函式實現排序的原理。
qsort() 在資料量小的情況下會優選選擇歸併排序演算法。當資料量比較大的時候,就會選擇快速排序演算法。使用快速排序的時候,對於分割槽點的選擇,qsort() 函式採用的正是三數取中法。
在快速排序中,如果區間的元素個數小於等於 4,qsort() 就會採用插入排序對資料排序。因為在小資料規模下, O(n^2) 的值要小於 O(n*logn)。
總結
本文創作靈感來源於 極客時間 王爭老師的《資料結構與演算法之美》課程,通過課後反思以及借鑑各位學友的發言總結,現整理出自己的知識架構,以便日後溫故知新,查漏補缺。
初入演算法學習,必是步履蹣跚,一路磕磕絆絆跌跌撞撞。看不懂別慌,也別忙著總結,先讀五遍文章先,無他,唯手熟爾~
與諸君共勉