這裏是傳送門⇒總結:關於排序算法html
|
平均時間複雜度 |
最優時間複雜度 |
最差時間複雜度 |
空間複雜度 |
穩定性 |
快速排序 |
O(nlogn) |
O(nlogn) |
O(n2) |
O(logn) |
不穩定 |
快速排序是對冒泡排序的一種改進,是一種劃分交換排序,採用了分治的思想算法
- 算法描述
- 從待排序列中選定一個基準數(好比序列的第一個數),把比基準數小的數放在左邊,比基準數大的放在右邊,同樣大的放哪邊都行(如下是讓其在左邊)。這個過程稱爲分區
- 而後再按照這種方式把左右兩個區間再分別進行分區
- 以此類推...
- 整個排序過程能夠遞歸進行(好像說遞歸有可能會爆棧)
- 分區的具體實現
- 總結起來就是「挖坑填數」
- 選定待排序列第一個數array[left]爲基準數,把array[left]的值保存在變量baseVal中
- 因爲array[left]的值已經被保存起來了,能夠理解爲array[left]的值被拿走了,如今array[left]上有一個坑,如今來找一個數填進來
- 這個坑在左邊,因此從後向前掃描待排序列array,用變量j記錄掃描位置
- 當發現此時的array[j]的值比基準值小或者等於基準數,把此時的array[j]的值填到坑裏,也就是讓a[left] = a[j]
- 如今的a[left]填完數了,但是a[j]的數被拿走填到a[left]了,如今變成a[j]有坑了,須要找另外一個數來填
- 這個時候坑在右邊了,從頭向後掃描待排序列array,用變量i記錄掃描位置
- 當發現此時的array[i]的值比基準值大,把此時的array[i]的值填到坑裏,也就是讓a[j] = a[i]
- 如今的a[j]填完數了,但是a[i]的數被拿走填到a[j]了,如今變成a[i]有坑了,須要找另外一個數來填
- 以此類推...
- 直到i和j相遇了,即左右兩邊的坑都填完了,只剩下中間的坑
- 最後把一開始保存在baseVal的值填進最後一個坑
- 也就是在左邊挖坑,去右邊找一個應該在左邊的數,填進坑裏,這個行爲致使右邊有坑,因此得去左邊找一個應該在右邊的數來填,而後又致使左邊有坑...最後剩一個坑,就把一開始保存的基準數填進去
- JS實現
// 此處傳入的array會被直接改變
function QuickSort(array, left, right) {
if (left < right) {
var baseVal = array[left];
var i = left,
j = right;
while (i < j) {
while (i < j && array[j] > baseVal) {
j--;
}
if (i < j) {
array[i++] = array[j];
}
while (i < j && array[i] <= baseVal) {
i++;
}
if (i < j) {
array[j--] = array[i];
}
}
array[i] = baseVal;
QuickSort(array, left, i - 1);
QuickSort(array, i + 1, right);
}
}
- 分析
- 快速排序的一次分區是從兩頭交替尋找合適的數,直到i,j相遇,因此每一次分區的時間複雜度都是O(n),那麼整個快速排序的時間複雜度與分區次數有關
- 最差狀況是每次分區選擇的基準數都是當前序列的最小值(即待排序列一開始就升序)的時候,這會致使每次分區後左區間的長度爲0,(其實就至關於冒泡了,每次只排好一個數)這種狀況下,長度爲n的待排序列將進行n-1次分區,那麼最差時間複雜度爲O(n2)
- 最優狀況是每次分區選擇的基準數剛好將當前序列幾乎等分,這種狀況下,長度爲n的待排序列將進行大概log2n次分區,那麼最優時間複雜度爲O(nlogn)
- 儘管快速排序只須要O(1)大小的輔助空間,但考慮遞歸深度,快速排序須要一個棧空間來實現遞歸。最好的狀況下,所需棧的最大深度爲log2(n+1),最優空間複雜度爲O(logn);但最壞的狀況下,所需棧的最大深度爲n,最差空間複雜度爲O(n)。快速排序的平均空間複雜度爲O(logn)
- 想知道怎麼由遞歸算法時間複雜度公式計算時間複雜度的,看這個別人家的博客
- 或者是這種利用遞歸樹來描述遞歸算法執行狀況的分析方式,看另一個別人家的博客
- 因爲鍵值的比較和交換是跳躍進行的,所以快速排序是一種不穩定的排序。假如以爲把鍵值比較條件
array[i] <= baseVal
改爲array[i] < baseVal
能夠變成穩定排序的話,請看這個例子[3,4,4,2,6]
- 優化
- 最理想的狀況是,選擇的基準數剛好能把待排序列分紅兩個幾乎等長的區間
- 而上面的方法是以待排序列的第一個數爲基準數
- 咱們能夠改變基準數的選擇方案來優化這個算法
- 隨機選取基準:取待排序列中任意一個元素做爲基準
- 三數取中:使用左端、右端和中心位置上的三個元素的中值做爲樞紐元
- 實現方法:按照上面的算法,只要讓選擇的基準和待排序列的第一個數字交換便可