1. 程式人生 > >資料結構與算法系列12--排序優化

資料結構與算法系列12--排序優化

如何選擇合適的排序演算法?**

這裡有前面講過的各種演算法是時間複雜度的對比圖,大家可以看看下。
在這裡插入圖片描述
如果我們要選擇一個通用的排序演算法,前面講到的線性排序演算法顯然是不合適的,因為它的適用場景非常有限,儘管他是速度最快的。
如果對於小規模資料可以選擇時間複雜度O(n^2的演算法),但是對於大規模資料,則應該選擇時間複雜度為O(nlogn)的演算法更加高效。所以為了相容任意規模的資料的排序,我們一般選擇時間複雜度為O(nlogn)的演算法。這裡時間複雜度為O(nlogn)的演算法有兩個,一個是快排,一個是歸併,但是我們更多的是選擇快排,為什麼呢?因為歸併排序並不是原地排序,它的空間複雜度是O(n),比如我們要排序200M的資料,除了資料本身佔用的記憶體外,還需要另外200M的資料儲存空間,空間耗費就翻倍了,這對於空間有限的機器來說,就完成不了。而快排本身是原地排序,不會佔用額外的儲存空間,是比較理想的選擇。

如何解決快排在最壞情況下時間複雜度為O(n)的問題?(優化快排)**

先說下為什麼最壞情況下快速排序的時間複雜度是O(n^2)呢?
如果資料原來就是有序的或者接近有序的,每次分割槽點都選擇最後一個數據,那快速排序演算法就會變得非常糟糕,時間複雜度就會退化為O(n2)。其實就是我們的分割槽節點選得不合理。
解決辦法就是選擇合適的節點
最理想的分割槽節點就是,被節點分開的兩個分割槽中,資料的數量差不多。
1.三數取中法
(1)從區間的首、中、尾分別取一個數,然後比較大小,取中間值作為分割槽點。
(2)如果要排序的陣列比較大,那“三數取中”可能就不夠用了,可能要“5數取中”或者“10數取中”。
2.隨機法:


每次從要排序的區間中,隨機選擇一個元素作為分割槽點。

特別注意:
使用快排還要注意一個問題,快排是通過遞迴實現的,我們知道遞迴有堆疊溢位的風險,所以使用快排要警惕這個問題的出現。為了防止遞迴過深而堆疊過小,而導致堆疊溢位,我們有兩種解決辦法:
1.限制遞迴深度。一旦遞迴過深,超過了我們事先設定的閾值,就停止遞迴
2.在堆上模擬實現一個函式呼叫棧,手動模擬遞迴壓棧、出棧過程,這樣就沒有系統棧大小的限制。

通用排序函式實現技巧

1.資料量不大時,可以採取用時間換空間的思路
2.資料量大時,優化快排分割槽點的選擇
3.防止堆疊溢位,可以選擇在堆上手動模擬呼叫棧解決
4.在排序區間中,當元素個數小於某個常數是,可以考慮使用O(n^2)級別的插入排序
5.用哨兵簡化程式碼,每次排序都減少一次判斷,儘可能把效能優化到極致

快排中為避免遞迴呼叫過深,所以在堆上模擬了棧。這是什麼意思呢?
其實就是將遞迴呼叫,改寫為迴圈非遞迴方式。