【数据结构与算法】排序

95 阅读3分钟

概述

最容易想到的排序,从旧数组中找到一个最小的,不断放入新的数组中。(不使用数组的slice等方法是因为会使效率变得更慢)

let arr = [3, 5, 6, 7, 1, 2, 4, 9, 8]

function getMin(arr) {
    if (arr == null || arr.length == 0) return
    let index = -1
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] != null && arr[i] < arr[index] || arr[i] != null && index == -1) {
            index = i
        }
    }
    let result = arr[index]
    arr[index] = null
    return result
}

function sort(arr) {
    let newArr = new Array(arr.length)
    for (let i = 0; i < newArr.length; i++) {
        newArr[i] = getMin(arr)
    }
    return newArr
}

console.log(sort(arr))

冒泡排序

任何排序的本质都是比较和交换。

let arr = [3, 5, 6, 7, 1, 2, 4, 9, 8]

function compare(b, a) {
    return b > a;
}

function exchange(arr, a, b) {
    let temp = arr[a]
    arr[a] = arr[b]
    arr[b] = temp
}

function sort(arr) {
    for (let j = 0; j < arr.length - 1; j++) {
        //  arr.length - 1 - i 单轮比较 索引到倒数第二个数
        //  外层循环影响内层循环 每次少比较 i 次(末尾的数不断确定)
        for (let i = 0; i < arr.length - 1 - i; i++) {
            if (compare(arr[i], arr[i + 1])) {
                exchange(arr, i, i + 1)
            }
        }
    }
}

sort(arr)
console.log(arr)

详解可见:【排序算法】史上最通俗易懂的【冒泡排序】详解-CSDN博客

选择排序

任何一种排序算法,都没有优劣之分,只有适不适合的场景。

越混乱越适合快速排序,越有序越适合冒泡排序。选择排序在性能上居中。

let arr = [3, 5, 6, 7, 1, 2, 4, 9, 8]

function compare(b, a) {
    return b > a;
}

function exchange(arr, a, b) {
    let temp = arr[a]
    arr[a] = arr[b]
    arr[b] = temp
}

function sort(arr) {
    for (let i = 0; i < arr.length - 1; i++) {
        let minIndex = i
        for (let j = i; j < arr.length; j++) {
            if (compare(arr[minIndex], arr[j])) {
                minIndex = j
            }
        }
        if (minIndex != i) {
            exchange(arr, i, minIndex)
        }
    }
}

sort(arr)
console.log(arr)

详解见:排序算法--选择排序--详解及代码_选择排序法-CSDN博客

快速排序(浪费性能但精简版)

以下版本浪费了性能,但是极大降低了理解难度。

思路分析:选一个中间值,然后遍历其他值,比中间值大的放一边,比中间值小的放另一边。再分别递归左右数组。

let arr = [3, 5, 6, 7, 1, 2, 4, 9, 8]

function quickSort(arr) {
    if (arr == null || arr.length == 0) return []
    let middle = arr[0]
    let left = []
    let right = []
    for (let i = 1; i < arr.length; i++) {
        if (arr[i] < middle) {
            left.push(arr[i])
        } else {
            right.push(arr[i])
        }
    }
    left = quickSort(left)
    right = quickSort(right)
    left.push(middle)
    return left.concat(right)
}
console.log(quickSort(arr))

快速排序(完整版)

整体思路:

随机找一个基准值,开始双指针遍历,分别在左指针找到一个大于基准值的数,在右指针找到一个小于基准值的数,并将他们交换位置。为左右指针的一次循环。

然后将左指针和基准值交换(这里注意判断相等的情况),此时由基准值分割了数组,一边小的,一边大的。

随后递归即可完成排序。

function swap(arr, i, j) {
    let temp = arr[i]
    arr[i] = arr[j]
    arr[j] = temp
}

function quickSort(s, l, r) {
    if (l < r) {
        let i = l, j = r, x = s[l];
        while (i < j) {
            while (i < j && s[j] > x) {
                j--;
            }
            while (i < j && s[i] <= x) {
                i++;
            }
            swap(s, i, j)
        }
        swap(s, l, i)
        quickSort(s, l, i - 1);
        quickSort(s, i + 1, r);
    }
}

// 使用示例
let arr = [1, 4, 3, 5, 2];
quickSort(arr, 0, arr.length - 1);
console.log(arr); // 输出 [1, 2, 3, 4, 5]

s[i] <= x 这里的 = 不能丢,否则边界判断出问题