基数排序
一、原理
☆思想:对输入序列按位分别进行桶排序,桶个数为10,即数字[0-9],分桶时相应位的数字即为桶索引,有 LSD (Least sgnificant digital) 和 MSD (Most sgnificant digital)两种方式。LSD 的排序方式由键值的最右边开始,而 MSD 则相反,由键值的最左边开始。
☆过程:
以 LSD 为例,按个位把输入序列放到十个桶内,桶内元素个位都一样,不用排序,再把所有桶合并,此时所有元素的个位是有序的,依次处理百位、千位……,直到最高 位,最终得到有序数组。
以 MSD 为例,使用数组嵌套实现桶,分两步:
- 按最高位把输入序列放到十个桶内,桶内元素最高位都一样,不用排序,位数减1,对长度大于1的数组递归分桶……,直到位数为0,依次处理最高位、次高位……,直到个位,此时表示桶的数组层层嵌套;
- 从最外层数组开始合并,最终得到单层有序数组。
二、实现代码
JavaScript 代码实现一,LSD:
function RadixSort(arr) { var brr = []; var index, maxValue, maxDigits, baseVal; var sortedIndex = 0, n = arr.length; //获取最大、最小元素 maxValue = arr[0]; for (var i = 0; i < n; i++) { if (maxValue < arr[i]) { maxValue = arr[i]; } } //最大值有几位 maxDigits = 0; while (Math.floor(maxValue) && (++maxDigits)) { maxValue /= 10; } //按位数从右到左排序 for (var start = 1; start <= maxDigits; start++) { //当前位基数 baseVal = Math.pow(10, start - 1); //清空临时桶 brr = []; //按当前基数放入桶内 for (var i = 0; i < n; i++) { index = Math.floor(arr[i] / baseVal) % 10; if (!brr[index]) { brr[index] = [arr[i]]; } else if (Array.isArray(brr[index])) { brr[index].push(arr[i]); } } //把排序结果复制到输入数组 sortedIndex = 0; for (var i = 0; i < brr.length; i++) { if (Array.isArray(brr[i])) { for (var j = 0; j < brr[i].length; j++) { arr[sortedIndex++] = brr[i][j]; } } } } }
JavaScript 代码实现二,MSD,一般用一个数组表示所有桶,用计数获取数组下标,此处使用数组嵌套实现桶,相对麻烦,性能可能不高,好在容易理解:
function RadixSort(arr) { var n = arr.length; var brr = []; //获取最大元素 maxValue = arr[0]; for (var i = 0; i < n; i++) { if (maxValue < arr[i]) { maxValue = arr[i]; } } //最大值有几位 maxDigits = 0; while (Math.floor(maxValue) && (++maxDigits)) { maxValue /= 10; } //递归分桶 brr = splitBucket(arr, maxDigits); //把桶内元素合并为单层数组 return mergeBucket(brr); } //对数组按右数第d位分桶 function splitBucket(arr, d) { var brr = []; //输入无效退出 if (d < 1 || arr.length < 2) return; //当前位基数 baseVal = Math.pow(10, d - 1); console.log(baseVal); //按当前基数放入桶内 for (var j = 0; j < arr.length; j++) { index = Math.floor(arr[j] / baseVal) % 10; //console.log(index); //空桶,转为数组加入 if (!brr[index]) { brr[index] = [arr[j]]; } else if (Array.isArray(brr[index])) { //brr[index].push(arr[j]); //加入数组最后 brr[index][brr[index].length] = arr[j]; } } console.log('div:' + brr); //位数递减 --d; //遍历数组,对其元素分桶 for (var j = 0; d >= 1 && j < brr.length; j++) { if (brr[j]&&brr[j].length > 1) { console.log('before:' + brr[j]); brr[j] = splitBucket(brr[j], d); console.log('after:' + brr[j]); } } //返回分桶后数组 return brr; } //数组扁平化,2020-01-16 function mergeBucket(arr) { var sortedIndex = 0; var zrr = []; var isarray = true; while (isarray) { sortedIndex = 0; zrr = []; isarray = false; for (var i = 0; i < arr.length; i++) { //元素为数组 if (Array.isArray(arr[i])) { isarray = true; //汇总所有子元素 for (var j = 0; j < arr[i].length; j++) { zrr[sortedIndex++] = arr[i][j]; } } else if (arr[i]) { zrr[sortedIndex++] = arr[i]; } } arr = zrr; } return arr; }
三、优化
无;
四、复杂度
| 名称 | 时间复杂度 | 空间复杂度 | 稳定性 | ||
| 平均 | 最坏 | 最优 | |||
| 基数排序 | O(n*d) | O(n*d) | O(n*d) | O(n+k) | √ |
最坏情况,所有元素分到一个桶内,时间复杂度取决于桶内排序使用的算法;
最优情况,桶大小为1,即只有一个值,复杂度为 O(n*d),d为元素最大值位数;
本排序需要申请的额外空间取决于输入内容是数字还是字母,或者二者混合,k为桶的数量或区间长度,数字为10,字母为52,混合为62;
相同元素不会交换,本排序稳定。
参考资料:

浙公网安备 33010602011771号