第十三章 排序算法 下部分

排序算法 下

如下被稱爲分佈式排序的算法,原始數組中的數據會分發到多箇中間結構(桶),再合起來放回原始數組。最著名的分佈式算法有計數排序、桶排序和基數排序,這三種算法很是類似。算法

計數排序

計數排序是一種用來排序整數的優秀算法,時間複雜度爲O(n + k),其中k是臨時計數數組的大小,可是,他須要更多的內存來存放臨時變量。typescript

  • 先建立一個排序數據最大值 + 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確保最大值也能加入數據分佈式

桶排序

桶排序也稱之爲箱排序,也是分佈式排序算法中的一種,它將元素分爲不一樣的桶(較小的數組),而後使用一個簡單的排序算法排序(好比說插入排序),而後合併數組爲結果code

對於桶排序算法,咱們須要指定須要多少個桶來排序各個元素,默認狀況下,咱們會使用5個桶(這裏我認爲是每一個桶的容量爲5,並且看這個做者命名bucketSize,也以爲就是桶容量,不過肯定了一個桶的容量,天然就肯定了桶的個數,後面我仍是以桶容量來講明該參數)。桶排序在全部元素平分到各個桶時的表現最好。若是元素很是稀疏,則使用更多的桶會更好。若是元素很是密集,則使用較少的桶會比較好,所以咱們容許bucketSize以參數的形式傳遞排序

  • 第一步: 建立桶ip

    建立桶看似很簡單,實際上仍是有點難度的,須要作到幾點內存

    • 算出桶的個數: Math.floor((最大值 - 最小值 ) / 單個桶的容量) + 1
    • 而後建立一個桶的個數對應大小的數組,內部填充[],注意這裏不能使用fill填充,fill填充的數組是同一個引用,因此會致使失敗(這裏建立了一個二維數組)
    • 將排序元素塞到對應的桶裏去: (當前值 - 最小值) / 單個桶的容量,其實這個塞入的過程就已經對桶內數據進行了一次粗略排序
  • 第二步: 對桶排序element

    傳回的是一個二維數組,桶是有序的(0號桶的內容會都小於1號桶的內容)get

    使用插入排序對桶內數據排序

    組合爲新的數組返回

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)

仍是想強調一下,填充桶數組的時候不能使用'fill'

相關文章
相關標籤/搜索