数据结构与算法之排序: 基数排序 (Javascript版)

排序

  • 排序:把某个乱序的数组变成升序或降序的数组 (这里用数组来做举例)

基数排序

  • 核心思想
    • 按照位数来进行分层,“个”, “十”, “百”, “千”, …
      • 得到数组中的最低位和最高位
      • 准备一个合适的数组 m (下标0-9对应当前位数的值)
    • 按照最低位优先的策略开始将当前位数上的值存入m对应下标的元素中(这个元素是数组或是链表, 但是一般而言我们处理的时候,使用计数排序中的累计数组来处理)
    • 将按照下标存储的当前位数的所有数据按下标顺序拍平处理,当前位数存储完毕,这时候只看当前位数是有序的
    • 得到新数组开始向下一级高位的处理,按照这个流程直到所有位数处理完毕,整个数组就有序了
  • 简单来说
    • 0-9准备10个桶
    • 将原数组元素先按照个位对应分配到桶里并收集 (数位较短的前面补0)
      • 在这个过程中,需要来回折腾数据
      • 处理过程如下代码所示
    • 再处理十位,百位,直到全部位数分配完成,收集后的数组就是最终有序的数组
  • 基数排序是桶排序的一种扩展

算法实现

function radixSort(list) {
	const len = list.length; // 获取数组长度
	const max = Math.max.apply(null, list); // 获取数组中最大值
	let i = 0; // 用于计数的数组
	let radix = 1; // 基数(个:1, 十:10, 百: 100, ...)
	const tmp = new Array(len); // 临时倒腾数组
	while(max / radix > 0) {
		// 初始化存储桶 0 ~ 9, 全0填充
		const bucket = new Array(10).fill(0);
		// 对当前基数下的值进行累加计算总数
		for(i=0; i<len; i++) {
			// 获取当前基数下的值, 也就是对应的桶的下标
			const currentRadixNum = Math.floor(list[i] / radix) % 10;
			bucket[currentRadixNum] ++; // 从 0 开始自增, 这里桶中存储的是当前下标下对应数据的累计个数
		}
		// 计算当前数据位置: 基于桶内每个下标对应数据的累计个数,计算当前元素在整个桶中的位置,并反向赋值给当前桶
		for(i = 1; i<10; i++) {
			bucket[i] += bucket[i-1];
		}
		// 将a[i]的值按照桶存储的位置倒腾到临时数组tmp中
		for(i = len-1; i>=0; i--) {
			const currentRadixNum = Math.floor(list[i] / radix) % 10; // 当前基数下的数字
			const currentIndex = bucket[currentRadixNum] - 1; // bucket存储的是位置,位置-1为下标
			tmp[currentIndex] = list[i]; // 根据桶存储的位置, 
			bucket[currentRadixNum] --; // 桶内当前元素累计值 自减
		}
		// 再将临时数组中的元素倒腾回list中,此时完成了一轮的基于基数的处理
		for(i=0; i<len; i++) {
			list[i] = tmp[i];
		}
		radix *= 10; // 基数进位处理
	}
}


const list = [100, 34, 1002, 1234, 66, 137, 49, 102, 9, 108, 102, 101, 104, 109, 102];
radixSort(list);
console.log(list); // [9, 34, 49, 66, 100, 101, 102, 102, 102, 104, 108, 109, 137, 1002, 1234]

总结

  • 基数排序时间复杂度 O(kn) ≈ O(n) k是常数
    • 最外层的 max/base 是常数级别, 内部的多个for循环也是常数,即: 常数 k
  • 基数排序也可理解为按关键字(这里的关键字就是基数radix)排序
  • 非比较排序,基于下标的顺序来回赋值, 使用计数排序的思想做累加数组
  • 基数排序基于计数排序,是桶排序的一种, 又称多关键字排序
  • 最好从个位(低位)先开始排起,这是一种比较简单的处理方式
    • 高位优先需要用到递归,就复杂一些
  • 是一种思想简单,实现起来有些复杂的算法
  • 因其基于计数排序,所以基数排序的适用场景也是基数排序需要考虑的场景

你可能感兴趣的:(Data,Structure,and,Algorithms,算法,排序,基数排序)