常见的排序算法(JavaScript版)

文章目录

    • 冒泡排序
    • 选择排序
    • 插入排序
    • 快速排序
    • 归并排序
    • 计数排序
    • 基数排序

1.冒泡排序

冒泡排序是一种非常基础的排序算法,它通过重复遍历要排序的数列,比较每对相邻元素的大小,并在必要时交换它们的位置。这个过程会重复进行,直到没有需要交换的元素为止,这意味着数列已经完全排序。

时间复杂度: O(n^2)

const sort = (arr) => {
    for(let i = 0; i < arr.length; i++) {
        for(let j = i + 1; j < arr.length;j++) {
            if(arr[i] > arr[j]) {
                [arr[i],arr[j]] = [arr[j],arr[i]] // 交换两个元素位置
            }
        }
    }
    return arr
}

2.选择排序

选择排序是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完.

时间复杂度: O(n^2)

const sort = (arr) => {

    for(let i = 0; i < arr.length - 1; i++) {
        let minIndex = i;
        for(let j = i + 1; j < arr.length; j++) {
            if(arr[j] < arr[minIndex]) {
                minIndex = j;
            }
            const temp = arr[i];
            arr[i] = arr[minIndex];
            arr[minIndex] = temp;
        }
    
    }
    return arr;
}

3.插入排序

插入排序是一种简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要移动元素以腾出空间。

时间复杂度: O(n^2)

onst sort = (arr) => {
    // 判断arr是否只有一个元素,是的话直接返回
    if (arr.length <= 1) {
        return arr;
    }
    for(let i = 1; i < arr.length; i++) {
        let item = arr[i];
        let j = i - 1;
        while(j >= 0 && arr[j] > item) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = item;
    }
    return arr
}

4.快速排序

快速排序是一种高效的排序算法,采用分治法(Divide and Conquer)策略来把一个序列分为较小的两个子序列,然后递归地排序两个子序列。

时间复杂度:O(n log n)

const sort = (arr) => {
    if(arr.length <= 1) return arr
    const mid = Math.floor(arr.length / 2)
    const midValue = arr.splice(mid,1)[0]
    const left = []
    const right = []
    for(let i = 0 ;i < arr.length;i++) {
        if(arr[i] < midValue) {
            left.push(arr[i])
        } else {
            right.push(arr[i])
        }
    }
    return sort(left).concat(midValue,sort(right))
}

5.归并排序

归并排序是一种使用分治法的排序算法,它将数组分成更小的数组,直到每个子数组只有一个元素,然后开始合并这些子数组,直到最终得到一个有序的数组。

时间复杂度:O(n log n)

const sort = (arr) => {
    // 判断数组元素是否小于2,如果小于,返回数组开始排序
    if(arr.length < 2) return arr

    // 获取数组中间索引
    const mid = Math.floor(arr.length / 2);
    const left = arr.slice(0,mid) // 左侧数组
    const right = arr.slice(mid) //右侧数组

    // 两个数组排合并为一个有序数组
    const merge = (leftArr,rightArr) => {
        let res = [] // 定义一个存储结果的数组
        while(leftArr.length && rightArr.length ) {
            // 判断左侧数组第一个元素是否小于右侧数组第一个元素
            if(leftArr[0] <= rightArr[0]) {
                res.push(leftArr.shift()) // 
            } else {
                res.push(rightArr.shift())
            }
        }
        return res.concat(leftArr).concat(rightArr)
    }
    // 递归调用并返回结果
    return merge(sort(left),sort(right))
}

6.计数排序

计数排序是一种非比较型整数排序算法,它适用于当整数的取值范围不大时使用。这种算法的核心思想是使用一个额外的数组(计数数组)来统计原数组中每个元素出现的次数,然后根据这些计数来确定每个元素在排序后数组中的位置。

时间复杂度:O(n + k)

const sort =  (arr) => {
    const min = Math.min(...arr);
    const max = Math.max(...arr);
    const count = new Array(max - min + 1).fill(0);
    let bias = 0;
    for (let i = 0; i < arr.length; i++) {
        count[arr[i] - min]++;
    }
    let index = 0;
    let i = 0;
    while (index < arr.length) {
        if(count[i] !== 0) {
            arr[index++] = i + min;
            count[i]--;
        } else {
            i++;
        }
    }
    return arr;
}

7.基数排序

归并排序是一种使用分治法策略的排序算法。它将原始数据分成更小的数组,递归地将这些小数组排序,然后将排序好的小数组合并成较大的有序数组,直到最终整个数组有序。

时间复杂度:O(nk)

const sort = (arr) => {
    if(arr.length < 2 || arr === null) return arr
    let max = Math.max(...arr) // 获取数组中的最大值
    let maxDigit = 0 // 计算最大排序次数
    while(max > 0) {
        maxDigit++
        max = Math.floor(max / 10)
    }
    const bucket = Array.from({length: 10}, () => []) // 创建10个桶
    let mod = 10,div = 1 // 计算当前位的基数
    for(let i  = 0; i < maxDigit; i++) {
        for(let j = 0 ; j < arr.length; j++) {
            const item = arr[j]
            const digit = Math.floor((item % mod) / div) // 获取当前位的数字
            bucket[digit].push(item)
        }
        let index = 0
        for(let i = 0; i < bucket.length; i++) {
            while(bucket[i].length > 0) {
                arr[index++] = bucket[i].shift()
            }
        }
        
        mod *= 10
        div *= 10
    }
    return arr
}

对比总结

以下是对冒泡排序、选择排序、插入排序、快速排序、归并排序、基数排序和计数排序的比较

排序算法时间复杂度空间复杂度稳定性是否基于比较适用场景
冒泡排序O(n^2)O(1)稳定小数据集或基本有序
选择排序O(n^2)O(1)稳定小数据集
插入排序O(n^2)O(1)稳定小数据集或部分有序
快速排序O(n log n)O(log n)不稳定大数据集,随机数据
归并排序O(n log n)O(n)稳定大数据集,有序数据
基数排序O(nk)O(n+k)稳定整数排序,k较小
计数排序O(n+k)O(k)稳定整数排序,k较小
  • 时间复杂度:大多数排序算法在最好情况下可以达到O(n log n),但快速排序、归并排序和基数排序在平均情况下也能保持这一性能。冒泡排序、选择排序和插入排序在最坏情况下都是O(n^2)。
  • 空间复杂度:原地排序算法(如冒泡排序、选择排序和插入排序)的空间复杂度为O(1),而归并排序和基数排序需要额外的空间。
  • 稳定性:稳定的排序算法(如冒泡排序、选择排序、插入排序和归并排序)保持相等元素的原始顺序,这对于某些应用非常重要。
  • 是否基于比较:基于比较的排序算法(如冒泡、选择和快速排序)通过元素之间的比较来确定顺序,而非基于比较的排序算法(如计数排序和基数排序)则不依赖于比较。
  • 适用场景:每种排序算法都有其适用的场景。例如,快速排序适合大数据集,而计数排序和基数排序适合于整数且值的范围较小的情况

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值