排序算法之快速排序

這裏是傳送門⇒總結:關於排序算法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]
  • 優化
    • 最理想的狀況是,選擇的基準數剛好能把待排序列分紅兩個幾乎等長的區間
    • 而上面的方法是以待排序列的第一個數爲基準數
    • 咱們能夠改變基準數的選擇方案來優化這個算法
    • 隨機選取基準:取待排序列中任意一個元素做爲基準
    • 三數取中:使用左端、右端和中心位置上的三個元素的中值做爲樞紐元
    • 實現方法:按照上面的算法,只要讓選擇的基準和待排序列的第一個數字交換便可
相關文章
相關標籤/搜索