第十三章 排序演算法 下部分
阿新 • • 發佈:2020-12-13
排序演算法 下部分
以下被稱為分散式排序的演算法,原始陣列中的資料會分發到多箇中間結構(桶),再合起來放回原始陣列。最著名的分散式演算法有計數排序、桶排序和基數排序,這三種演算法非常相似。
計數排序
計數排序是一種用來排序整數的優秀演算法,時間複雜度為O(n + k),其中k是臨時計數陣列的大小,但是,他需要更多的記憶體來存放臨時變數.
- 先建立一個排序資料中最大值 + 1建立一個數組(關於這部分我剛開始也不是很明白,js的陣列不是動態的嗎!何必多此一舉去找排序資料的最大值,然後建立一個數組呢???後來想了一下陣列的擴容是不是要資料遷移,那樣的確不如提前建立大一點,然後找了一下資料: 探究JS V8引擎下的“陣列”底層實現
- 然後開始計數,其實就是以下標代表資料,然後以值作為資料出現的次數(分散式中的第一步)
- 迴圈計數的陣列,然後將其迴歸到原陣列上(分散式中的第二步)
export function sortArray(arr: Array<number>) { if (arr.length < 2){ return; } let len = findMaxNumber(arr) + 1; let countArr = new Array(len).fill(0); for (let i = 0; i < arr.length; i++) { countArr[arr[i]]++; } for (let i = 0,index = 0; i < countArr.length; i++) { for (let j = 0; j < countArr[i]; j++) { arr[index++] = i; } } } /** * 找最大數 * @param arr */ function findMaxNumber(arr: Array<number>):number { let max:number = 0; for (let i = 0; i < arr.length; i++) { if(arr[i] > max){ max = arr[i]; } } return max; } let arr = [54,8,45,7, 1,2,45,9,8,452,35,754,127,6,21,124,454] sortArray(arr) // @ts-ignore console.log(arr)
可以看出這種排序很適合那種有多個重複資料的整數陣列,但是資料中有一個特別大的時候,計數陣列將會佔用很大的記憶體
還有這裡建立的陣列是 最大值 + 1,因為陣列的下標是0開始的,所以 +1確保最大值也能加入資料
桶排序
桶排序
也稱之為箱排序
,也是分散式排序演算法中的一種,它將元素分為不同的桶(較小的陣列),然後使用一個簡單的排序演算法排序(比如說插入排序),然後合併陣列為結果對於桶排序演算法,我們需要指定需要多少個桶來排序各個元素,預設情況下,我們會使用5個桶(這裡我認為是每個桶的容量為5,而且看這個作者命名
bucketSize
,也覺得就是桶容量,不過確定了一個桶的容量,自然就確定了桶的個數,後面我還是以桶容量來說明該引數).桶排序在所有元素平分到各個桶時的表現最好.如果元素非常稀疏,則使用更多的桶會更好.如果元素非常密集,則使用較少的桶會比較好,因此我們允許bucketSize
以引數的形式傳遞
-
第一步: 建立桶
建立桶看似很簡單,實際上還是有點難度的,需要做到幾點
- 算出桶的個數:
Math.floor((最大值 - 最小值 ) / 單個桶的容量) + 1
- 然後建立一個
桶的個數
對應大小的陣列,內部填充[]
,注意這裡不能使用fill
填充,fill
填充的陣列是同一個引用,所以會導致失敗(這裡建立了一個二維陣列) - 將排序元素塞到對應的桶裡去:
(當前值 - 最小值) / 單個桶的容量
,其實這個塞入的過程就已經對桶內資料進行了一次粗略排序
- 算出桶的個數:
-
第二步: 對桶排序
傳回的是一個二維陣列,桶是有序的(0號桶的內容會都小於1號桶的內容)
使用
插入排序
對桶內資料排序組合為新的陣列返回
import {sortArray as insertSort} from "./InsertSort"
export function sortArray(arr: Array<number>, bucketSize: number = 5) {
if (arr.length < 2) {
return arr;
}
// 建立桶
let buckets = createBuckets(arr, bucketSize);
// 桶排序並返回陣列
return sortBuckets(buckets);
}
/**
* 建立桶
* @param arr
* @param bucketSize 單個桶的容量
*/
function createBuckets(arr: Array<number>, bucketSize: number):any[][] {
let minNum = arr[0];
let maxNum = arr[0];
// 迴圈查詢最大值最小值
for (let i = 1; i < arr.length; i++) {
if (maxNum < arr[i]) {
maxNum = arr[i];
} else if (arr[i] < minNum) {
minNum = arr[i];
}
}
// 計算桶數
const bucketCount = Math.floor((maxNum - minNum) / bucketSize) + 1;
// 建立並初始化裝桶的陣列,這裡就是一個二元陣列了
const buckets = [];
//這個for迴圈不要使用fill替換
for (let i = 0; i < bucketCount; i++) {
buckets[i] = [];
}
for (let i = 0; i < arr.length; i++) {
// 計算下標的過程其實是對資料進行了一次簡單的排序,然後桶就會呈現出有序性
const index = Math.floor((arr[i] - minNum) / bucketSize)
buckets[index].push(arr[i]);
}
return buckets;
}
/**
* 對桶進行排序
* @param buckets
*/
function sortBuckets(buckets: any[][]) {
let result = [];
for (let i = 0; i < buckets.length; i++) {
let element = buckets[i];
if(element !== null){
insertSort(element)
result.push(...element);
}
}
return result;
}
let arr = [54,8,45,7, 1,2,45,9,8,452,35,754,127,6,21,124,454]
let sortArr = sortArray(arr)
// @ts-ignore
console.log(sortArr)