以前一篇文章經常使用的比較算法排序總結介紹了幾種經常使用的比較排序算法,下面介紹的是幾種非比較排序算法,分別是:計數排序、基數排序以及桶排序。javascript
非比較排序算法內部引用的都是計數排序,固然你也能夠將計數排序換爲其餘的比較排序算法。html
計數排序的步驟爲:java
計數排序的原理用一個通俗的栗子來說就是這樣的:算法
// 有一個這樣的數組 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);
基數排序的基本原理是:數據結構
12
和112
這兩個數字,則當作012
和112
仍是用一個栗子來講明一下,這樣更加清楚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