[路飞]_老白的算法学习笔记04

393 阅读4分钟

「这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战

哈,大家早上好呀,大家可以看到本系列现在已经加上路飞前缀,不是因为我是一个海贼迷,这是大佬 全栈然叔 发起的一个坚持学习系列,为了积极响应,我会在今后一段时间的文章中都加上路飞字样,和掘金的小伙伴们一起进步,那还不飞龙升天,哈哈。

今天我们来学习最后两个经典的非比较型排序算法。

  • 基数排序(radixSort)
  • 桶排序(bucketSort)

本篇文章中的代码在这里.

基数排序(radixSort)

基数排序是也是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于基数排序是基于计数排序,所以也是一种稳定排序。

时间复杂度

实现基数排序的方式有两种

方法一:时间复杂度O(d * (n + k)) d是最大值的位数,k是进制。

方法二: 时间复杂度为O(dn)

算法步骤

  1. 找出待排数组中最大的值
  2. 从个位上的数字0~9进行计数(0-9)排序
  3. 从十位、百位、... 直到最大值位数进行 2步骤

图解过程

![radixSort](/Users/fin-9061/Library/Mobile Documents/comappleCloudDocs/knowledge/algorithms/常用的排序算法.assets/radixSort.gif)

代码实现

方法一:直接利用之前的计数排序

function radixSort(arr) {
  let max = arr[0]
  for (let i = 0; i < arr.length; i++) { // 找到最大值
    if (arr[i] > max) max = arr[i]
  }
  for (let i = 1; i <= max; i *= 10) {
    countingSort(arr, i) // 从小到大对基数进行排序
  }
  function countingSort(arr, divider) {
    const counts = new Array(10).fill(0)
    for (let i = 0; i < arr.length; i++) {
      counts[Math.floor(arr[i] / divider) % 10]++ // 保存对应位数的基数个数
    }
    for (let j = 1; j < counts.length; j++) {
      counts[j] += counts[j - 1] // 把整个保存的个数连续起来,以便后面推算出对应值在有序数组中的位置,这一步不懂的请一步到白的算法学习笔记03中的计数数组的改进版进行学习
    }
    const res = []
    for (let k = arr.length - 1; k >= 0; k--) {
      res[--counts[Math.floor(arr[k] / divider) % 10]] = arr[k] // 根据关系arr元素在res中的对应关系进行赋值
    }
    for (let i = 0; i < res.length; i++) {
      arr[i] = res[i] // 把有序数列复制给arr
    }
  }
}

// const radixArr = getRandomArr();
const radixArr = [999999, 888888, 7, 9, 323, 454, 33333, 1, 2]
console.log('before radix sorting ===>', radixArr)
radixSort(radixArr)
console.log('after radix sorting ===>', radixArr)


方法二:利用二维数组存放相同基数的值

function radixSort2(arr) {
  let max = arr[0]
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] > max) max = arr[i]
  }
  const counter = [] //存放排好基数位置的二维数组
  for (let i = 1; i < max; i *= 10) {
    let index = 0
    // 进行基数位排序
    for (let j = 0; j < arr.length; j++) {
      const radix = Math.floor(arr[j] / i) % 10
      if (!counter[radix]) counter[radix] = []
      counter[radix].push(arr[j])
    }
    // 对arr进行重新赋值
    for (let k = 0; k < counter.length; k++) {
      while (counter[k] && counter[k].length > 0) {
        arr[index++] = counter[k].shift()
      }
    }
  }
}
const radixArr2 = [999999, 888888, 7, 9, 323, 454, 33333, 1, 2]
console.log('before radix2 sorting ===>', radixArr2)
radixSort2(radixArr2)
console.log('after radix2 sorting ===>', radixArr2)


桶排序(Bucket Sort)

桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效,我们需要做到这两点:

  1. 在额外空间充足的情况下,尽量增大桶的数量
  2. 使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中(数据均匀分配到每个桶中最快,分配到同一个桶中最慢)

同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要。

算法步骤

  1. 创建一定数量的桶(比如用数组,链表作为桶)
  2. 按照一定的规则(不同类型的数据,规则不同),将序列中的元素均匀分配到对应的桶
  3. 分别对每个桶进行单独排序
  4. 将所有非空桶的元素合并成有序序列

图解过程

![20190219081232815](/Users/fin-9061/Library/Mobile Documents/comappleCloudDocs/knowledge/algorithms/常用的排序算法.assets/bucketSort.png)

代码实现

由于桶排序比较灵活,在这里我们只举个例子

function bucketSort(arr, size) {
  if (arr.length === 0) {
    return arr
  }

  let minValue = arr[0]
  let maxValue = arr[0]
  for (let i = 1; i < arr.length; i++) {
    if (arr[i] < minValue) {
      minValue = arr[i] // 输入数据的最小值
    }
    if (arr[i] > maxValue) {
      maxValue = arr[i] // 输入数据的最大值
    }
  }

  //桶的初始化
  let DEFAULT_BUCKET_SIZE = 5 // 设置桶的默认数量为5
  const bucketSize = size || DEFAULT_BUCKET_SIZE
  let bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1
  let buckets = new Array(bucketCount)
  for (let i = 0; i < buckets.length; i++) {
    buckets[i] = []
  }

  //利用映射函数将数据分配到各个桶中
  for (let i = 0; i < arr.length; i++) {
    buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i])
  }

  arr.length = 0
  for (let i = 0; i < buckets.length; i++) {
    insertionSort(buckets[i]) // 对每个桶进行排序,这里使用了插入排序
    for (let j = 0; j < buckets[i].length; j++) {
      arr.push(buckets[i][j])
    }
  }

  return arr
}
const bucketArr = getRandomArr()
console.log('before bucket sorting ===>', bucketArr)
bucketSort(bucketArr)
console.log('after bucket sorting ===>', bucketArr)

好了,到此我们一共用了四篇文章,学习了常用的10种经典排序方法,小伙伴们都学会了吗?没有学会的话文章多读几遍,有问题欢迎来留言区留言。

如果觉得还不错,可以点赞,或者关注我,我会持续更新前端和算法学习的文章。

参考内容

github.com/hustcc/JS-S…