编辑代码

//程序运行完成时一定要有输出语句,本工具才能正确展示运行结果。 

// 1、选择排序
function selectSort(arr) {
    // 缓存数组长度
    const len = arr.length
    // 定义 minIndex,缓存当前区间最小值的索引,注意是索引
    let minIndex
    // i 是当前排序区间的起点
    for (let i = 0; i < len - 1; i++) {
        // 初始化 minIndex 为当前区间第一个元素
        minIndex = i
        // i、j分别定义当前区间的上下界,i是左边界,j是右边界
        for (let j = i; j < len; j++) {
            // 若 j 处的数据项比当前最小值还要小,则更新最小值索引为 j
            if (arr[j] < arr[minIndex]) {
                minIndex = j
            }
        }
        // 如果 minIndex 对应元素不是目前的头部元素,则交换两者
        if (minIndex !== i) {
            [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]
        }
    }
    return arr
}

// 2、冒泡排序
function bubbleSort(arr) {
    // 外层循环用于控制从头到尾的比较+交换到底有多少轮
    for (let i = 0; i < arr.length; i++) {
        // 内层循环用于完成每一轮遍历过程中的重复比较+交换
        for (let j = 0; j < arr.length - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
            }
        }
    }
    return arr
}

// 3、直接插入排序
// function insertSort(arr) {
//     for (let i = 1; i < arr.length; i++) {
//         for (let j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
//             [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
//         }
//     }
//     return arr
// }
function insertSort(arr) {
    // 缓存数组长度
    const len = arr.length
    // temp 用来保存当前需要插入的元素
    let temp
    // i用于标识每次被插入的元素的索引
    for (let i = 1; i < len; i++) {
        // j用于帮助 temp 寻找自己应该有的定位
        let j = i
        temp = arr[i]
        // 判断 j 前面一个元素是否比 temp 大
        while (j > 0 && arr[j - 1] > temp) {
            // 如果是,则将 j 前面的一个元素后移一位,为 temp 让出位置
            arr[j] = arr[j - 1]
            j--
        }
        // 循环让位,最后得到的 j 就是 temp 的正确索引
        arr[j] = temp
    }
    return arr
}

// // 4、归并排序
// function mergeSort(arr) {
//     function merge(left, right) {
//         const temp = []
//         while (left.length && right.length) {
//             if (left[0] < right[0]) {
//                 temp.push(left.shift())
//             } else {
//                 temp.push(right.shift())
//             }
//         }
//         return temp.concat(left, right)
//     }

//     function sort(arr) {
//         if (arr.length === 1) {
//             return arr
//         }
//         const mid = Math.floor(arr.length / 2)
//         return merge(sort(arr.slice(0, mid)), sort(arr.slice(mid)))
//     }
//     return sort(arr)
// }
function mergeSort(arr) {
    function mergeArr(arr1, arr2) {
        // 初始化两个指针,分别指向 arr1 和 arr2
        let i = 0, j = 0
        // 初始化结果数组
        const res = []
        // 缓存arr1的长度
        const len1 = arr1.length
        // 缓存arr2的长度
        const len2 = arr2.length
        // 合并两个子数组
        while (i < len1 && j < len2) {
            if (arr1[i] < arr2[j]) {
                res.push(arr1[i])
                i++
            } else {
                res.push(arr2[j])
                j++
            }
        }
        // 若其中一个子数组首先被合并完全,则直接拼接另一个子数组的剩余部分
        if (i < len1) {
            return res.concat(arr1.slice(i))
        } else {
            return res.concat(arr2.slice(j))
        }
    }
    const len = arr.length
    // 处理边界情况
    if (len <= 1) {
        return arr
    }
    // 计算分割点
    const mid = Math.floor(len / 2)
    // 递归分割左子数组,然后合并为有序数组
    const leftArr = mergeSort(arr.slice(0, mid))
    // 递归分割右子数组,然后合并为有序数组
    const rightArr = mergeSort(arr.slice(mid, len))
    // 合并左右两个有序数组
    arr = mergeArr(leftArr, rightArr)
    // 返回合并后的结果
    return arr
}




// 5、快速排序
function quickSort(arr, left = 0, right = arr.length - 1) {
    // 定义递归边界,若子数组只有一个元素,则没有排序必要
    if (arr.length > 1) {
        // 计算当前数组的基准值
        const nextPivot = partition(arr, left, right)
        // 如果左边子数组的长度不小于1,则递归快排这个子数组
        if (left < nextPivot - 1) {
            quickSort(arr, left, nextPivot - 1)
        }
        // 如果右边子数组的长度不小于1,则递归快排这个子数组
        if (nextPivot < right) {
            quickSort(arr, nextPivot, right)
        }
    }
    return arr
}

// 寻找基准值的过程
function partition(arr, left, right) {
    // 基准值默认值
    // const pivot = arr[left];
    const pivot = arr[Math.floor(left + (right - left) / 2)]
    // const pivot = getPivot(arr, left, right);
    // 当左右指针不越界时,循环执行以下逻辑
    while (left <= right) {
        // 左指针所指元素若不大于基准值,则右移左指针
        while (arr[left] < pivot) {
            left++
        }
        // 右指针所指元素若不小于基准值,则左移右指针
        while (arr[right] > pivot) {
            right--
        }
        // 若i<=j,则意味着基准值左边存在较大元素或右边存在较小元素,交换两个元素确保左右两侧有序
        if (left <= right) {
            [arr[left], arr[right]] = [arr[right], arr[left]];
            left++
            right--
        }
    }
    // 返回左指针索引作为下一个基准值的索引
    return left;
}

// 注意:Partition函数中 key<=a[right] 以及 key>=a[left] 表达式必须包含等于的判断,否则当数组两头的数相等时将会造成死循环  例如 {5,2,6,2,9,10,5}

function getPivot(a, left, right) {//三值取中
    let mid = left + Math.floor((right - left) / 2);
    if (a[mid] > a[right])
        [a[mid], a[right]] = [a[right], a[mid]];//交换
    if (a[left] > a[right])
        [a[left], a[right]] = [a[right], a[left]];//交换
    if (a[mid] > a[left])
        [a[left], a[mid]] = [a[mid], a[left]];//交换
    let pivot = a[left];//现在a[mid]<a[left]<a[right];
    return pivot;
}

// // 5、快速排序(in-place 实现)
// function quickSort2(arr) {
//     function partition(arr, left, right) {
//         let pivot = arr[left]
//         let storeIndex = left

//         for (let i = left + 1; i <= right; i++) {
//             if (arr[i] < pivot) {
//                 storeIndex++
//                 [arr[storeIndex], arr[i]] = [arr[i], arr[storeIndex]]
//             }
//         }

//         [arr[left], arr[storeIndex]] = [arr[storeIndex], arr[left]]

//         return storeIndex
//     }

//     function sort(arr, left, right) {
//         if (left < right) {
//             let storeIndex = partition(arr, left, right)
//             sort(arr, left, storeIndex - 1)
//             sort(arr, storeIndex + 1, right)
//         }
//     }

//     sort(arr, 0, arr.length - 1)

//     return arr
// }

// 6、希尔排序
function shellSort(arr) {
    let len = arr.length
    // 逐步降低步长直至为1为止
    for (let gap = Math.floor(len / 2); gap > 0; gap = Math.floor(gap / 2)) {
        // 内层循环与插入排序的写法基本一致,只是每次移动的步长变为gap
        for (let i = gap; i < arr.length; i++) {
            let j = i;
            let cur = arr[i]
            while (j - gap >= 0 && cur < arr[j - gap]) {
                arr[j] = arr[j - gap]
                j = j - gap
            }
            arr[j] = cur
        }
    }
    return arr
}

// 7、堆排序
function heapSort(arr) {
    // 堆排序,反复向下对比+交换,low表示下界,high表示上界
    function heapify(low, high) {
        // 初始化 i 为当前结点,j 为当前结点的左孩子
        let i = low, j = i * 2 + 1
        // 当 j 不超过上界时,重复向下对比+交换的操作
        while (j <= high) {
            // 如果右孩子比左孩子更大,则用右孩子和根结点比较
            if (j + 1 <= high && arr[j + 1] > arr[j]) {
                j = j + 1
            }

            // 若当前结点比孩子结点小,则交换两者的位置,把较大的结点“拱上去”
            if (arr[i] < arr[j]) {
                // 交换位置
                [arr[i], arr[j]] = [arr[j], arr[i]]
                // i 更新为被交换的孩子结点的索引
                i = j
                // j 更新为孩子结点的左孩子的索引
                j = j * 2 + 1
            } else {
                break
            }
        }
    }
    const len = arr.length
    // 初始化堆排序,从第一个非叶子结点开始
    for (let i = Math.floor((len / 2) - 1); i >= 0; i--) {
        heapify(i, len - 1)
    }
    // 排序,每一次for循环找出一个当前最大值,数组长度减一
    for (let j = len - 1; j >= 0; j--) {
        // 从根结点开始调整,并且最后一个结点已经为当前最大值,不需要再参与比较,
        // 所以第三个参数为j - 1,即比较到最后一个结点前即可
        [arr[0], arr[j]] = [arr[j], arr[0]]
        heapify(0, j - 1)
    }
    return arr
}

// 8、基数排序 
// digit 最大位数
function radixSort(arr, digit) {
    let a = 10
    let b = 1
    const counter = []
    for (let i = 0; i < digit; i += 1, a *= 10, b *= 10) {
        // 向各个桶中添加元素
        for (let j = 0; j < arr.length; j++) {
            const bucket = Math.floor((arr[j] % a / b))
            if (!counter[bucket]) {
                counter[bucket] = []
            }
            counter[bucket].push(arr[j])
        }
        let c = 0
        // 将已经根据相应位数排好的桶的元素导回arr中
        for (let k = 0; k < counter.length; k++) {
            let value
            if (counter[k]) {
                while ((value = counter[k].shift()) !== undefined) {
                    arr[c++] = value
                }
            }
        }
    }
    return arr
}

// let arr = [6, 7, 3, 4, 1, 5, 9, 2, 8];
let arr = [5, 2, 6, 2, 9, 10, 5]
console.log(mergeSort(arr))