三種非比較排序算法總結

以前一篇文章經常使用的比較算法排序總結介紹了幾種經常使用的比較排序算法,下面介紹的是幾種非比較排序算法,分別是:計數排序、基數排序以及桶排序javascript

非比較排序算法內部引用的都是計數排序,固然你也能夠將計數排序換爲其餘的比較排序算法。html

計數排序

計數排序的步驟爲:java

  1. 遍歷數組(A),藉助一個輔助數組(B),將每個數字放在輔助數組(B)對應索引的位置並計數加1
  2. 遍歷輔助數組(B),將每項的值變爲與前一項相加的和
  3. 遍歷原始數組(A),取出輔助數組中對應的索引值,將值填入對應的一個新的數組(C)中

計數排序的原理用一個通俗的栗子來說就是這樣的:算法

// 有一個這樣的數組
var arr = [1, 5, 3, 8, 2];

// 8排在哪一個位置? 4 ,如何得來?

1 < 8 // 計數加1
5 < 8 // 計數加1
3 < 8 // 計數加1
2 < 8 // 計數加1

那最後天然是 4

// 算法上是這樣實現:
B: [, 1, 1, 1, , 1, , , 1]
B: [0, 1, 1, 1, 0, 1, 0, 0, 1]  // 空餘部分用0填充
B: [0, 1, 2, 3, 3, 4, 4, 4, 5]  // 計數

// 遍歷原始數組
arr[0]: 1
// 取B中對應的索引值
B[1]: 1
// 放入C
C: [, 1]

// 中間省略N步

// N+1步
arr[4]: 2
B[2]: 2
C: [, 1, 2, 3, 5, 8]
C: [1, 2, 3, 5, 8]  // 去除空餘

實現:數組

// 分類 ------------ 內部非比較排序
// 數據結構 --------- 數組
// 最差時間複雜度 ---- O(n + k)
// 最優時間複雜度 ---- O(n + k)
// 平均時間複雜度 ---- O(n + k)
// 所需輔助空間 ------ O(n + k)
// 穩定性 ----------- 穩定

var arr = [1, 4, 5, 2, 3, 9, 0, 7, 6];
var b = [];
var c = [];

// 初始計數
for (var i = 0; i < arr.length; i++) {
    var ai = arr[i];
    var count = b[ai];
    count ? b[ai] = ++count : b[ai] = 1;
}

// 計數
for (var i = 1; i < b.length; i++) {
    var p = b[i - 1];
    var n = b[i];

    if (!p) {
        p = 0;
    }

    if (!n) {
        n = 0;
    }

    b[i] = p + n;
}

// 從新分配
for (var i = arr.length - 1; i >= 0 ; i--) {
    var index = b[arr[i]];
    while (index >= 0) {
        if (typeof c[index] === 'number') {
            index--;
        } else {
            c[index] = arr[i];
            break;
        }
    }
}

// 去除空格
for (var i = 0; i < c.length; i++) {
    if (!c[i]) {
        c.splice(i, 1);
    }
}

console.log(c);

基數排序

基數排序的基本原理是:數據結構

  1. 將全部待比較的數字均當作位數相同的,不一樣的用0填充,好比12112這兩個數字,則當作012112
  2. 從最後位置依次向前比較,每次比較會獲得一個排序(這裏的比較會運用到計數排序),這樣就會獲得最終的排序規則

仍是用一個栗子來講明一下,這樣更加清楚code

// 有這樣一個數組

var arr = [12, 112, 34, 26, 290, 1, 45];

// 1. 補齊
var arr = [012, 112, 034, 026, 290, 001, 045]

// 2. 比較最後一位
012         290         
112         001
034         012
026   ->    112
290         034
001         045
045         026

// 3. 比較第二位
290         001         
001         012
012         112
112   ->    026
034         034
045         045
026         290

// 4. 比較百位
001         001         1   
012         012         12
112         026         26
026   ->    034    ->   34
034         045         45
045         112         112
290         290         290

實現:htm

// 分類 ------------- 內部非比較排序
// 數據結構 ---------- 數組
// 最差時間複雜度 ---- O(n * max)
// 最優時間複雜度 ---- O(n * max)
// 平均時間複雜度 ---- O(n * max)
// 所需輔助空間 ------ O(n * max)
// 穩定性 ----------- 穩定

var arr = [12, 112, 34, 26, 290, 1, 45];
var max = 1;

// 獲取每次的餘數
function getRemainder(n, d) {
    var base = Math.pow(10, d);
    return Math.floor(n / base) % 10;
}

function countingSort(arr, d) {
    var b = [];
    var c = [];

    for (var j = 0; j < arr.length; j++) {
        var l = getRemainder(arr[j], d);
        var count = b[l];
        count ? b[l] = ++count : b[l] = 1;
    }

    // 計數
    for (var i = 1; i < b.length; i++) {
        var p = b[i - 1];
        var n = b[i];

        if (!p) {
            p = 0;
        }

        if (!n) {
            n = 0;
        }

        b[i] = p + n;
    }

    // 從新分配
    for (var i = arr.length - 1; i >= 0 ; i--) {
        var index = b[getRemainder(arr[i], d)];
        while (index >= 0) {
            if (typeof c[index] === 'number') {
                index--;
            } else {
                c[index] = arr[i];
                break;
            }
        }
    }

    // 去除空格
    for (var i = 0; i < c.length; i++) {
        if (!c[i]) {
            c.splice(i, 1);
        }
    }

    for (var i = 0; i < c.length; i++) {
        arr[i] = c[i];
    }
}

// 計算最大位數
for (var i = 0; i < arr.length; i++) {
    var t = '' + arr[i];
    if (t.length > max) {
        max = t.length;
    }
}

for (var i = 0; i < max; i++) {
    countingSort(arr, i);
}

console.log(arr);

大體運行過程以下:blog

12 112 34 26 290 1 45
290 1 12 112 34 45 26
1 12 112 26 34 45 290
1 12 26 34 45 112 290

桶排序

桶排序是原理是,將一個數組分紅若干個桶(桶的數量根據數據量來肯定,好比根據最大值、值的區間範圍等等,但儘可能保證每一個桶內的數據均勻便可),經過必定的規則來肯定這個數字在哪一個桶當中,好比下面的我就取的每一個桶的範圍爲10,那麼除以這個範圍便可以得出這個數字屬於哪一個桶中。而後再採用非比較排序或者計數排序對桶內數據進行排序,這樣在遍歷全部桶中的數據時,就保證了數據已經排列好了。排序

下面仍是以一個數組爲例說明:

var arr = [12, 112, 34, 32, 29, 26, 290, 114, 1, 45, 292];

12 / 10  -> 1
112 / 10 -> 11
34 / 10  -> 3
32 / 10  -> 3
29 / 10  -> 2
26 / 10  -> 2
290 / 10 -> 29
114 / 10 -> 11
1 / 10   -> 0
45 /10   -> 4

0號桶內數據:1
1號桶內數據:12
2號桶內數據:29, 26
3號桶內數據: 34, 32
4號桶內數據:45
11號桶內數據:112, 114
29號桶內數據:290, 292

得出了每一個桶內的數據以後,而後在按照基本的排序把桶內數據排序好便可,可見桶的複雜度取決於取桶的數量以及桶內的排序算法

// 分類 ------------- 內部非比較排序
// 數據結構 --------- 數組
// 最差時間複雜度 ---- O(nlogn)或O(n^2),只有一個桶,取決於桶內排序方式
// 最優時間複雜度 ---- O(n),每一個元素佔一個桶
// 平均時間複雜度 ---- O(n),保證各個桶內元素個數均勻便可
// 所需輔助空間 ------ O(n + bn),bn爲桶的個數
// 穩定性 ----------- 穩定

var arr = [12, 112, 34, 32, 29, 26, 290, 114, 1, 45, 292];

function countingSort(arr) {
    var b = [];
    var c = [];

    for (var i = 0; i < arr.length; i++) {
        var ai = arr[i];
        var count = b[ai];
        count ? b[ai] = ++count : b[ai] = 1;
    }

    // 計數
    for (var i = 1; i < b.length; i++) {
        var p = b[i - 1];
        var n = b[i];

        if (!p) {
            p = 0;
        }

        if (!n) {
            n = 0;
        }

        b[i] = p + n;
    }

    // 從新分配
    for (var i = arr.length - 1; i >= 0 ; i--) {
        var index = b[arr[i]];
        c[index] ? c[index - 1] = arr[i] : c[index] = arr[i];
    }

    // 去除空格
    for (var i = 0; i < c.length; i++) {
        if (!c[i]) {
            c.splice(i, 1);
        }
    }

    for (var i = 0; i < c.length; i++) {
        arr[i] = c[i];
    }
}

function bucketSort(arr) {
    var bucketList = [];
    var resultList = [];
    var base = 10;

    // 分桶
    for (var i = 0; i < arr.length; i++) {
        var bucketNum = Math.floor(arr[i] / 10);
        if (bucketList[bucketNum]) {
            bucketList[bucketNum].push(arr[i]);
        } else {
            bucketList[bucketNum] = [arr[i]];
        }
    }

    // 桶內使用計數排序
    for (var i = 0; i < bucketList.length; i++) {
        bucketList[i] && countingSort(bucketList[i]);
    }

    // 輸出
    for (var i = 0; i < bucketList.length; i++) {
        if (bucketList[i]) {
            resultList = resultList.concat(bucketList[i]);
        }
    }

    return resultList;
}

console.log(bucketSort(arr));

過程大體以下:

0號桶內數據: 1
1號桶內數據: 12
2號桶內數據: 29 26
3號桶內數據: 34 32
4號桶內數據: 45
11號桶內數據: 112 114
29號桶內數據: 290 292

參考

相關文章
相關標籤/搜索