十种排序算法(JS实现)

225 阅读3分钟

排序算法

梳理下之前学习的排序算法

1. 冒泡排序

重复比较相邻元素,将大元素放在右边(小元素在左边,冒泡)

  • 从左边开始。依次比较相邻元素大小,如果左边的元素大,则交换元素。(将最大元素置后)

  • 重复步骤 1 n-1(数组长度-1) 次。(因为大元素已经置后,所以在重复时可以不需要在比较后排元素)

function bubbleSort(arr) {
    for (let i = 0, len = arr.length; i < len - 1; i++) {
        for (let j = 0; j < len - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                const temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp; 
            }
        }
    }
}

2. 选择排序

重复遍历数组,依次标记最小元素位置置前

  • 遍历数组,标记当前最小元素位置

  • 交换第一个元素和最小元素位置

  • 重复步骤 1~2,依次找出最小元素

function selectionSort(arr) {
    for (let i = 0, len = arr.length; i < len - 1; i++) {
        let min = i;

        for (let j = i + 1; j < len; j++) {
            if (arr[min] > arr[j]) {
                min = j;
            }
        }
        
        const temp = arr[i];
        arr[i] = arr[min];
        arr[min] = temp; 
    }
}

3. 插入排序

  • 从左边第二个元素开始,依次和前面所有元素比较

  • 如果元素比前面元素小,则交换元素

  • 重复步骤 2,直到前面无元素或者比前面元素大

function insertionSort(arr) {
    for (let i = 1, len = arr.length; i < len; i++) {
        for (let j = i; j > 0; j--) {
            if (arr[j] < arr[j - 1]) {
                const temp = arr[j];
                arr[j] = arr[j - 1];
                arr[j - 1] = temp; 
            } else {
                break;
            }
        }
    }
}

4. 希尔排序

插入排序的改进,将插入排序的与前面元素比较改为与间隔元素比较

  • 从左边第 n 个元素开始,依次比较间隔为 n 的元素

  • 如果元素比前面元素小,则交换元素

  • 不断减小 n 的值,直到为 1,重复步骤 1~2

function shellSort(arr) {
    for (let gap = Math.floor(arr.length / 2); gap > 0; gap = Math.floor(gap / 2)) {
        for (let i = gap, len = arr.length; i < len; i++) {
            for (let j = i; j > 0; j = j - gap) {
                if (arr[j] < arr[j - gap]) {
                    const temp = arr[j];
                    arr[j] = arr[j - gap];
                    arr[j - gap] = temp;                         
                } else {
                    break;
                }
            }
        }
    }
}

5. 归并排序

归并排序的核心思想是分治

  • 将数组等分两份 n / 2,分别排序左右数组再合并,递归直到 n < 2

  • 合并时通过比较两个数组前端元素大小,再决定将哪个元素置前

  • 重复步骤 1~2

function mergeSort(arr) {
    const len = arr.length;

    const merge = (left, right) => {
        let result = [];

        while(left.length || right.length) {
            if (!left.length) {
                result = result.concat(right);
                break;
            }

            if (!right.length) {
                result = result.concat(left);
                break;
            }


            if (left[0] <= right[0]) {
                result.push(left.shift())
            }

            if (left[0] > right[0]) {
                result.push(right.shift())
            }
        }

        return result;
    }

    if (len < 2) {
        return arr;
    }

    const mid = Math.floor(len / 2);
    const left = mergeSort(arr.slice(0, mid));
    const right = mergeSort(arr.slice(mid));

    return merge(left, right);
}

6. 快速排序

快速排序的核心思想是分治

  • 选择基准 a[0],遍历其余元素,对元素和基准进行比较大小

  • 将比基准大的值放在左边,比基准小的值放右边

  • 对左边右边元素分别重复步骤 1~2

function quickSort(arr) {
    if (arr.lenght < 2) {
        return arr;
    }

    const base = arr[0];
    let left = [];
    let right = [];

    for (let i = 1, len = arr.length; i < len; i++) {
        const cur = arr[i];

        if (cur <= base) {
            left.push(cur);
        } else {
            right.push(cur);
        }
    }


    if (left.length > 1) left = quickSort(left);
    if (right.length > 1) right = quickSort(right);

    return [...left, base, ...right];
}

7. 堆排序

堆排序的核心思想是构建二叉树(节点元素大于子元素)

忘记怎么创建二叉树了,留着以后补上

8. 计数排序

空间换时间的计数

  • 遍历原始数组,将元素出现的次数存入计数数组,其中索引为元素值

  • 遍历计数数组,重复取出存入的元素(根据计数)

function countingSort(arr) {
    const countArr = [];
    const sortedArr = [];

    for (let i = 0, len = arr.length; i < len; i++) {
        const cur = arr[i];

        if (countArr[cur]) {
            countArr[cur] += 1;
        } else {
            countArr[cur] = 1;
        }
    }

    for (let i = 0, len = countArr.length; i < len; i++) {
        while (countArr[i]) {
            sortedArr.push(i);
            countArr[i] -= 1;
        }
    }

    return sortedArr;
}

9. 桶排序

设定一定数量得桶,将不同数据放置于不同得桶中分别排序再连接

  • 将数据映射到不同得桶

  • 桶内排序

  • 将不同桶得数据连接即可

function bucketSort(arr, bucketSize) {
    if (arr.length < 2) {
        return arr;
    }

    const result = [];

    const min = Math.min.apply(null, arr);
    const max = Math.max.apply(null, arr);

    bucketSize = bucketSize || 5;

    const step = Math.floor((max - min) / bucketSize);
    const buckets = [];

    // 映射
    for (let i = 0, len = arr.length; i < len; i++) {
        const cur = arr[i];
        const index = Math.floor(cur / step);

        buckets[index] = buckets[index] || [];

        buckets[index].push(cur);
    }

    for (let i = 0, len = buckets.length; i < len; i++) {
        if (buckets[i]) {
            insertionSort(buckets[i]);

            while(buckets[i].length) {
                result.push(buckets[i].shift())
            }
        }

    }

    return result;
}

10. 基数排序

从低位排序到高位排序

  • 从低位开始,遍历数据的同一位,按照计数排序一样的方式置于数组

  • 重复步骤 1

function radixSort(arr, maxDigit) {
    let mod = 10;
    let dev = 1;

    for (let i = 1; i <= maxDigit; i++, dev *= 10, mod *= 10) {
        const digitArr = [];

        for (let j = 0, len = arr.length; j < len; j++) {
            const num = parseInt((arr[j] % mod) / dev)

            digitArr[num] = digitArr[num] || [];

            digitArr[num].push(arr[j]);
        }

        arr = [];

        for (let j = 0, len = digitArr.length; j < len; j++) {
            const curArr = digitArr[j] || [];

            for (let n = 0, lenA = curArr.length; n < lenA; n++) {
                arr.push(curArr[n]);
            }
        }
    }

    return arr;
}