排序優化——如何實現一個通用的、高性能的排序函數
幾乎全部的編程語言都會提供排序函數,好比 C 語言的 qsort(), C++ STL 中的 sort(),這些排序函數是如何實現的呢?
1. 如何選擇合適的排序算法?
若是要實現一個通用的高效率的排序函數,咱們應該選擇那種排序算法呢?
- 線性排序算法的時間複雜度比較低,適用場景特殊,所以不適合做爲通用的排序函數。
- 小規模數據能夠選擇時間複雜度爲 $O(n^2)$ 的算法,大規模數據選擇時間複雜度爲 $O(nlogn)$ 的算法則會更加高效。爲了兼顧任意規模的數據,通常會首選複雜度爲 $O(nlogn)$ 的算法來實現排序函數。
- 歸併排序雖然最好狀況、最壞狀況和平均狀況下時間複雜度均可以作到 $O(nlogn)$,但它不是原地排序算法,空間複雜度爲 $O(n)$,排序的時候須要的額外空間和源數據通常大,空間消耗太高。
2. 如何優化快速排序?
快速排序最壞狀況下時間複雜度退化爲 $O(n^2)$ ,咱們怎樣來避免這種狀況的發生呢?
- 實際上,這種 $O(n^2)$ 複雜度出現的主要緣由仍是分區點選取得不合理。
- 理想的分區點應該是,被分區點分開的兩個區間,數據的數量差很少。
2.1. 分區點優化問題
- 三數取中法。從待排序數據首、尾、中分別取出一個數,而後對比大小,以這三個數的中間值做爲分區點。
- 若是排序數據比較多,可能要「五數取中」或者「十數取中」。
- 隨機法。每次從要排序的區間隨機選擇一個元素做爲分區點,這種狀況下,就能夠避免每次分區點都選得很是糟糕。
2.2. 堆棧溢出問題
快速排序是利用遞歸來實現的,當遞歸的的深度過深時,就會致使堆棧溢出。
- 限制遞歸深度。當遞歸次數超過咱們設定的閾值時,就中止遞歸。
- 在堆上模擬函數調用棧。手動模擬遞歸壓棧出棧過程,解除系統棧大小的限制。
3. C 語言的 qsort() 函數?
- qsort 優先使用歸併排序,在數據規模比較小的時候,以空間換時間。
- 當數據規模比較大時,qsort 會改成快速排序,以三數取中法來選取分區點。
- qsort 經過在堆上模擬函數調用棧來解決堆棧溢出問題。
- 當快速排序區間內元素小於等於 4 時,qsort 退化爲插入排序。由於在小數據規模下, $O(n^2)$ 時間複雜度算法並不必定比 $O(nlogn)$ 的算法執行時間長。
參考資料-極客時間專欄《數據結構與算法之美》算法
獲取更多精彩,請關注「seniusen」!
編程
歡迎關注本站公眾號,獲取更多信息