桶排序(基数排序)js

桶排序顾名思义,桶是装东西的
这里是把需要排序的东西,根据某种映射关系和桶关联起来,再放进去
那么桶的选择就非常重要,比如有一个很多大数的数组,我们要用桶排序
需要建立若干个桶形成映射,当然这不太合适,所以这种场景下就不要使用桶排序了

简单举个例子,就能理解桶排序了
假设有个待排序数组:[100,101,045,021]
其中个位最大的是是5,建立一个下标能到5的桶
[[],[],[],[],[],[]]
1.根据个位[[100],[101, 21],[],[],[],[45]]

2.从左到右倒出来[100,101,21,45],此时个位上有序

看十位建桶
3.根据十位[[100,101],[],[21],[],[45]]

4.从左到右倒出来[100,101,21,45], 此时个位十位上有序

5.根据百位[[21,45],[100,101]]

6.从左到右倒出来[21,45,100,101] ,此时数组有序

为什么都是从右到左倒出来
因为是升序的数组
每一次排序会让数组在某个位上有序
比如第一次让个位有序了,不能让十位倒出来的时候从右往左
要么一直从右往左,降序
要么从左往右,升序
基数排序
基数排序的思想是将整数按位数切割成不同的数字,
然后按每个位数分别比较从而得到有序的序列。

是不是和上面的桶排序很像,因为基数排序是桶排序的扩展
function radixSort(arr) {
  if (arr===null || arr.length<2) {
    return
  }

  radixSortMain(arr, 0, arr.length-1, maxBitsLen(arr))
}

// 获取arr中的最大值的有多少十进制位数
function maxBitsLen(arr) {
  let max = Number.MAX_VALUE
  arr.forEach(element => {
    max = Math.max(max, element)
  });

  // 也可以使用如下方法获取最大值
  // Math.max 获取任意个值,返回其中的最大值
  // 1. Math.max(...arr)
  // 2/ Math.max(null, arr)
  let len = 0
  while (max > 0) {
    len++
    max /= 10
  }
  return len
}


/**
 * 
 * @param {*} num 原始值
 * @param {*} index 要取哪位数,0是个位,1是十位
 */
function getDigit(num, index) {
  // %10只会取到0~9如果一个数不做处理,就会取到那个数的个位
  // 一个数除以10相当于把十位换到个位上再余10,就是十位上的数
  // >>0取整
  return (num/Math.pow(10, index)%10)>>0
}


function radixSortMain(arr, L, R, len) {
  // 10为基底
  const radix = 10
  let i = 0
  let j = 0
  const bucket = []
  for (let t = 0; t < len; t++) { // len有多少就需要排序多少次,
  // 分别排序个位,十位,百位等,t控制的当前的位数是在个位还是十位...
    const count = Array(10).fill(0) // 统计某位上的数的个数,
    // 所以这个count数组是一个最大长度为10,统计0~9在某位出现次数的数组
    // 比如 个位上有3个1,则count[1]最后是3

    // 统计某位上,某个数出现的次数
    for(i = L; i<=R ; i++) {
			j = getDigit(arr[i], t) // t如果是0取出个位,是1取出十位
			count[j]++
		} 
    for(i = 1;i<radix;i++) {
			// 累加和,在某位上小于等于i的数有多少个
			// 累加和形成片区
      // 之后count[1]表示小于等于1的个数
      // count[2]表示小于等于2的个数
      // 但是由于某个数出现的次数是不变的,所以会生成一个数的片区
      // 比如[4,4,4,2,2,1,1]
      // 4出现了3次,2出现2次,1出现2次
      // count[4] 是3,count[2]是2,count[1] 是2
      // 通过这个循环后变成[0,2,4,4,7,7.7,7,...]
      // 这样下面的循环就可以生成一个0~2是1的片区,3~4是2的片区,5~7是4的片区
			count[i] = count[i] + count[i-1]
		}
    // 从右往左遍历,因为要符合上面形成的片区
    // 第一次即个位上的数可以从左往右,因为此时数组还是无序的
    // 但是后面就要从右往左了,因为是在个位上有序的
		for(i=R;i>=L;i--) {
			j=getDigit(arr[i], t) // 拿到当前的数
			bucket[count[j]-1] = arr[i] // 填到对应的位置
			count[j]--
		}
    // 经过上面两个循环就可以把bucket变成[1,1,2,2,4,4,4]
		// 上面执行完,相当于出桶了
		// 维持最新状态
    // console.log(bucket)
		for(i=L,j=0;i<=R;i++,j++) {
			arr[i] = bucket[j]
		}
  }
}

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

radixSort(arr)
console.log(arr)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值