js實現快速排序(in-place)簡述

快速排序,又稱劃分交換排序。以分治法爲策略實現的快速排序算法。
javascript

本文主要要談的是利用javascript實現in-place思想的快速排序前端

 

分治法:java

計算機科學中,分治法是建基於多項分支遞歸的一種很重要的算法範式。字面上的解釋是「分而治之」,就是把一個複雜的問題分紅兩個或更多的相同或類似的子問題,直到最後子問題能夠簡單的直接求解,原問題的解即子問題的解的合併。(摘自維基百科)算法

 

快速排序的思想數組

數組中指定一個元素做爲標尺,比它大的放到該元素後面,比它小的放到該元素前面,如此重複直至所有正序排列。數據結構

 

快速排序分三步:函數

  1. 選基準:在數據結構中選擇一個元素做爲基準(pivot)
  2. 劃分區:參照基準元素值的大小,劃分無序區,全部小於基準元素的數據放入一個區間,全部大於基準元素的數據放入另外一區間,分區操做結束後,基準元素所處的位置就是最終排序後它應該所處的位置
  3. 遞歸:對初次劃分出來的兩個無序區間,遞歸調用第 1步和第 2步的算法,直到全部無序區間都只剩下一個元素爲止。

 

如今先說說廣泛的實現方法(沒有用到原地算法)性能

function quickSort(arr) {
    if (arr.length <= 1) return ;
    
    //取數組最接近中間的數位基準,奇數與偶數取值不一樣,但不印象,固然,你能夠選取第一個,或者最後一個數爲基準,這裏不做過多描述
    var pivotIndex = Math.floor(arr.length / 2);
    var pivot = arr.splice(pivotIndex, 1)[0];
    //左右區間,用於存放排序後的數
    var left = [];
    var right = [];

    console.log('基準爲:' + pivot + ' 時');
    for (var i = 0; i < arr.length; i++) {
        console.log('分區操做的第 ' + (i + 1) + ' 次循環:');
        //小於基準,放於左區間,大於基準,放於右區間
        if (arr[i] < pivot) {
            left.push(arr[i]);
            console.log('左邊:' + (arr[i]))
        } else {
            right.push(arr[i]);
            console.log('右邊:' + (arr[i]))
        }
    }
    //這裏使用concat操做符,將左區間,基準,右區間拼接爲一個新數組
    //而後遞歸1,2步驟,直至全部無序區間都 只剩下一個元素 ,遞歸結束
    return quickSort(left).concat([pivot], quickSort(right));
}

var arr = [14, 3, 15, 7, 2, 76, 11];
console.log(quickSort(arr));
/*
 * 基準爲7時,第一次分區獲得左右兩個子集[ 3, 2,]   7   [14, 15, 76, 11];
 * 以基準爲2,對左邊的子集[3,2]進行劃分區排序,獲得[2] 3。左子集排序所有結束
 * 以基準爲76,對右邊的子集進行劃分區排序,獲得[14, 15, 11] 76
 * 此時對上面的[14, 15, 11]以基準爲15再進行劃分區排序, [14, 11] 15
 * 此時對上面的[14, 11]以基準爲11再進行劃分區排序, 11  [14]
 * 全部無序區間都只剩下一個元素,遞歸結束
 *
 */

經過斷點調試,可得出結果。優化

 

弊端:ui

它須要Ω(n)的額外存儲空間,跟歸併排序同樣很差。在生產環境中,須要額外的內存空間,影響性能。

同時,不少人認爲上邊的就是真正的快速排序了。 因此,在下面,頗有必要的推薦in-place算法的快速排序

有關於原地算法可參考維基百科,被牆的同窗,百度也差很少。

 

in-place

快速排序通常是用遞歸實現,最關鍵是partition分割函數,它將數組劃分爲兩部分,一部分小於pivot,另外一部分大於pivot。具體原理上邊提過

function quickSort(arr) {
    // 交換
    function swap(arr, a, b) {
        var temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }

    // 分區
    function partition(arr, left, right) {
        /**
         * 開始時不知最終pivot的存放位置,能夠先將pivot交換到後面去
         * 這裏直接定義最右邊的元素爲基準
         */
        var pivot = arr[right];
        /**
         * 存放小於pivot的元素時,是緊挨着上一元素的,不然空隙裏存放的多是大於pivot的元素,
         * 故聲明一個storeIndex變量,並初始化爲left來依次緊挨着存放小於pivot的元素。
         */
        var storeIndex = left;
        for (var i = left; i < right; i++) {
            if (arr[i] < pivot) {
                /**
                 * 遍歷數組,找到小於的pivot的元素,(大於pivot的元素會跳過)
                 * 將循環i次時獲得的元素,經過swap交換放到storeIndex處,
                 * 並對storeIndex遞增1,表示下一個可能要交換的位置
                 */
                swap(arr, storeIndex, i);
                storeIndex++;
            }
        }
        // 最後: 將pivot交換到storeIndex處,基準元素放置到最終正確位置上
        swap(arr, right, storeIndex);
        return storeIndex;
    }

    function sort(arr, left, right) {
        if (left > right) return;

        var storeIndex = partition(arr, left, right);
        sort(arr, left, storeIndex - 1);
        sort(arr, storeIndex + 1, right);
    }

    sort(arr, 0, arr.length - 1);
    return arr;
}

console.log(quickSort([8, 4, 90, 8, 34, 67, 1, 26, 17]));

 

分區的優化

這裏細心的同窗可能會提出,選取不一樣的基準時,是否會有不一樣性能表現,答案是確定的,但,由於,我是搞前端的,對算法不是很瞭解,因此,這個坑留給厲害的人來填補。

 

複雜度

快速排序是排序速度最快的算法,它的時間複雜度是O(log n)

在平均情況下,排序n個項目要Ο(n log n)次比較。在最壞情況下則須要Ο(n2)次比較.

相關文章
相關標籤/搜索