javascript中可能用到的算法排序

算法複雜度

不是科班生的我,第一次看見時間複雜度之類的名詞表示很懵逼,因此就找了網上的資料補習了下:html

  1. 時間複雜度:是指執行算法所須要的計算工做量git

  2. 空間複雜度:是指算法在計算機內執行時所需存儲空間的度量github

  3. 排序算法穩定性: 假定在待排序的記錄序列中,存在多個具備相同的關鍵字的記錄,若通過排序,這些記錄的相對次序保持不變,即在原序列中,ri=rj,且ri在rj以前,而在排序後的序列中,ri仍在rj以前,則稱這種排序算法是穩定的;不然稱爲不穩定的。算法

這裏不詳細說 shell

參考:算法的時間複雜度和空間複雜度-總結理解排序算法的穩定性算法和算法的複雜度segmentfault

排序算法

排序算法

名詞解釋數組

n:數據規模函數

k:「桶」的個數ui

In-place:佔用常數內存,不佔用額外內存spa

Out-place:佔用額外內存

下面的算法實現升序

冒泡排序

顧名思義,從第一個開始比較相鄰的兩個,小的數網上冒。

實現

function bubleSort (arr) {
    var len = arr.length;
    var temp;
    for (var i=0; i<len-1; i++) {
        for(var j=0; j<len-1-i; j++) {
            //前一個大於後一個則交換位置
            if (arr[j] > arr[j+1]) {
                temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }
    return arr
}

選擇排序

假設第一個最小, 日後尋找比它小的,記下其index,一輪結束後將index上的數與開始假定的數交換位置。

實現

function selectionSort (arr) {
    var len = arr.length;
    var minIndex, temp;
    for (var i=0; i<len-1; i++) {
        minIndex = i;
        for (var j=i+1; j<len; j++) {
            if (arr[minIndex] > arr[j]) {
                minIndex = j
            }
        }
        temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
    }
    return arr;
}

插入排序

直接插入排序

打撲克的同志應該比較好理解。假設第一個元素是已經排序好的,將後一個元素提取出來,往前依次比較,比本身大的數日後挪,插入到第一次碰見比本身小的元素後面,組成一個新的序列。

實現
function insertionSort (arr) {
    var len = arr.length;
    var current, preIndex;
    for (var i=1; i<len; i++) {
        preIndex = i - 1;
        current = arr[i];
        while (preIndex>=0 && current < arr[preIndex]) {
            arr[preIndex+1] = arr[preIndex];
            preIndex--;
        }
        arr[preIndex+1] = current;
    }
    return arr
}

希爾排序

實質爲分組插入排序。爲了方便理解,借用網上某哥的圖,參考連接在下文。
希爾排序

由於是在已經分組排序過的基礎上進行插入排序,因此效率高。

實現
//由於核心是插入排序,因此咱們改造直接插入排序
function directInsertionSort(arr, gap) {
    var len = arr.length;
    var current, preIndex;
    for (var i=gap; i<len; i++) {
        current = arr[gap];
        preIndex = i - gap;
        while (preIndex>=0 && arr[preIndex] > current) {
            arr[preIndex+gap] = arr[preIndex];
            preIndex -= gap;
        }
        arr[preIndex+gap] = current;
    }
    return arr;
}
//編寫希爾排序函數
function shellSort (arr) {
    var len = arr.length;
    var gap = 1;
    //設置gap(希爾增量),這裏咱們給出比較經常使用的h值爲h = 3 * h + 1
    while (gap < len/3) {
        gap = gap * 3 + 1;
    }
    for (gap; gap>0; gap = Math.floor(gap/3)) {
        directInsertSort(arr, gap);
    }
    return arr;
}

碰見的問題,關於參數的傳遞:函數參數的傳遞能夠分爲按值傳遞和引用傳遞。
步長序列能夠看一下wiki

折半插入排序

相似直接插入,後一個元素(拿來比較的元素)與已排序的中間值m = (i-1) >> 1(位移運算,至關於Math.floor((i-1)/2))進行比較,若是i上的值大於m上的值,則與高半區折半比較,最終將比i上值高的區域日後移,將i上的值插入。如

arr = [2, 6, 7, 6, 8]   
//前三個是已經排好的。
//range = [low, high] = [0, 2], i = 3, current = arr[i]
// m = 1, arr[i] >= arr[m], rang = [2, 2]
// m = 2, arr[i] < arr[m]
// 變換位置 ==> arr = [2, 6, 6, 7, 8]
...
...
實現
function binaryInsertionSort (arr) {
    var len = arr.length;
    var low, height, current, m;
    for (var i=1; i<len; i++) {
        current = arr[i];
        low = 0;
        height = i-1;
        while (low <= height) {
            m = (low + height) >> 1;
            if (current >= arr[m]) {// = 是爲了保證穩定性
                low = m + 1;
            }else {
                height = m - 1;
            }
        }
        for (var j=i; j>low; j--) {
            arr[j] = arr[j-1];
        }
        arr[low] = current;
    }
    return arr;
}

歸併排序

採起分而治之的思想。遞歸分組、比較產生兩個已排序序列,再依次比較兩組開頭元素,較小的元素放入申請的新數組中。
歸併函數能夠經過遞歸、迭代實現。

遞歸

主要作的兩件事就是分解、合併(下面並非按照執行順序,只是思路):

[3, 5, 6, 2, 9]
--------------------------------------
分:   [3, 5]         [6, 2, 9]  
     [3]  [5]      [6]   [2, 9]
                        [2]  [9] 
--------------------------------------
合:                      [2, 9]
       [3, 5]         [2, 6, 9]   
          [2, 3, 5, 6, 9]
實現
function merge (left, right) {
    var result = [];
    while (left.length && right.length) {
        var item = left[0] <= right[0] ? left.shift() : right.shift();
        result.push(item);
    }
    return result.concat(left.length ? left : right);
}
function mergeSort (arr) {
    var len = arr.length;
    if (len === 1) {
        return arr;
    }
    var m = len >> 1;
    var left = arr.slice(0, m);
    var right = arr.slice(m);
    return merge(mergeSort(left), mergeSort(right))
}

遞歸可能會形成堆棧溢出的問題。

迭代

主要作的兩件事就是分解、合併(下面並非按照執行順序,只是思路):

[3, 5, 6, 2, 9]
--------------------------------------
分:  [3]  [5]  [6]  [2]  [9] 
--------------------------------------
合:     [3, 5]  [2, 6]  [9]
           [2, 3, 5, 6] [9]
           [2, 3, 5, 6, 9]
實現
function merge (left, right) {
    var result = [];
    while (left.length && right.length) {
        var item = left[0] <= right[0] ? left.shift() : right.shift();
        result.push(item);
    }
    return result.concat(left.length ? left : right);
}
function mergeSort (arr) {
    var len = arr.length;
    var result = [];
    //分組,每組有i個元素
    for (var i=1; i<=len; i*=2) {
        //比較相鄰兩組,有一組剩餘就退出
        for (var j=0; j+i<len; j+=2*i) {
            left = arr.slice(j, j+i);
            right = arr.slice(j+i, j+2*i);
            result = merge(left, right);
            arr.splice(j, 2*i, ...result)
        }
    }
    return arr
}

快速排序

快速排序是一種分而治之思想在排序算法上的典型應用。本質上來看,快速排序應該算是在冒泡排序基礎上的遞歸分治法。
步驟:選擇一個元素做爲基準,下面選擇第一個,依次比較後面的元素,將小於基準的元素放在基準的左邊,剩餘放右邊。造成左右兩個分區,再遞歸按以前的步驟排序。

實現

function swap (arr, i, j) {
    var temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
function partition (arr, left, right) {
    var pivot = left;
    var index = left + 1;
    for (var i=index; i<=right; i++) {
        if (arr[i] < arr[pivot]) {
            swap(arr, index, i);
            index++;
        }
    }
    swap(arr, index-1, pivot);
    return index-1
}
function quickSort (arr, left, right) {
    var len = arr.length;
    var partitionIndex;
    left = typeof left === 'number' ? left : 0;
    right = typeof right === 'number' ? right : len-1;
    if (left < right) {
        partitionIndex = partition (arr, left, right);
        quickSort(arr, left, partitionIndex-1);
        quickSort(arr, partitionIndex+1, right);
    }
    return arr;
}

快速排序排序效率很是高. 雖然它運行最糟糕時將達到O(n²)的時間複雜度, 但一般, 平均來看, 它的時間複雜爲O(nlogn), 比一樣爲O(nlogn)時間複雜度的歸併排序還要快. 快速排序彷佛更偏心亂序的數列, 越是亂序的數列, 它相比其餘排序而言, 相對效率更高.

Chrome的v8引擎爲了高效排序, 在排序數據超過了10條時, 便會採用快速排序. 對於10條及如下的數據採用的即是插入排序.

參考

十大經典排序算法
JS中可能用獲得的所有的排序算法

相關文章
相關標籤/搜索