计数排序的稳定版本
在学习基数排序之前,需要先了解计数排序的稳定版本。
之前了解的计数排序,只是朴素版的计数排序,除此之外,还有稳定版的计数排序。
朴素版的计数排序,只是给整数进行排序,当然没有什么问题,但是在一些给学生考试分数排序类似的场景中,这种要求保证排序后,数组中相等元素原本的先后顺序不变的,举个例子,也就是说,对于两个相同的数来说,在输入数组中先出现的数,在输出数组中也位于前面。
成绩表,图源自小灰的文章
如果是朴素版的计数排序,计数数组得到的结果是这样的。
朴素版计数排序得到的计数数组,图源自小灰的文章
而如果是稳定版的计数排序,计数数组会从第二个元素开始,每个元素都加上前面的所有元素之和。
这样做的目的是让计数数组中存储的数值,等于相应整数的最终排序位置。
比如下图中,下标为9的数值是5,代表整数9的排序位置为第5位。
稳定版计数排序得到的计数数组,图源自小灰的文章
接着创建一个输出数组,与需要排序的数值数组大小一致,再遍历开头的成绩表,从后向前得到所有成绩单的名次。
小绿成绩是95,则计数数组中下标为5的值——4,排名4就是小绿的名次,将小绿结果写入结果数组,并将计数数组下标5的值进行减1(代表假如下次遇到相同成绩时,排名为4-1=3)
小绿成绩排序,图源自小灰的文章
小白成绩是94,则计数数组中下标为4的值——2,排名2就是小白的名次,将小白结果写入结果数组,并将计数数组下标4的值进行减1(代表假如下次遇到相同成绩时,排名为2-1=1)
小白成绩排序,图源自小灰的文章
后续遍历过程以此类推,最后就能得到小红和小绿清楚的排序了。
小红和小绿的排序,图源自小灰的文章
以上便是计数排序的稳定版。
什么是基数排序
基数排序是按照数字的”位”,也称有效位和基数,来进行排序。
位是进制中的位,比如十进制的基数是10,百进制的基数是100……,所以我们才能按照个十百千万等的位来进行排序。
基数排序不仅可以为一组给定的手机号进行排序,也可以对英文单词进行排序。这些复杂的元素组合排序,基数排序会把排序工作拆分成多个阶段,每个阶段只对一个字符或数值进行计数排序,排序轮次跟元素长度相同。
例如有以下几个字母。
每轮分别对个位、十位、百位的字母(根据字母的ascii码数值大小),每一位进行一次计数排序,就是基数排序的流程。这种基数排序也叫MSD(从高位优先进行排序),同等的,另外还有一种称为LSD(从低位优先进行排序)。
如果遇到字符数量不同的单词,例如
这里最长的单词,company,字符长度是7,则对其它不足字符长度7的单词末尾进行补零。
排序时,把0作为最小的数值即可。
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
|
export function findMaxValue(array, compareFn = defaultCompare) { if (array && array.length > 0) { let max = array[0]; for (let i = 1; i < array.length; i++) { if (compareFn(max, array[i]) === Compare.LESS_THAN) { max = array[i]; } } return max; } return undefined; }
export function findMinValue(array, compareFn = defaultCompare) { if (array && array.length > 0) { let min = array[0]; for (let i = 1; i < array.length; i++) { if (compareFn(min, array[i]) === Compare.BIGGER_THAN) { min = array[i]; } } return min; } return undefined; }
const getBucketIndex = (value, minValue, significantDigit, radixBase) => Math.floor(((value - minValue) / significantDigit) % radixBase);
const countingSortForRadix = (array, radixBase, significantDigit, minValue) => { let bucketsIndex; const buckets = []; const aux = []; for (let i = 0; i < radixBase; i++) { buckets[i] = 0; } for (let i = 0; i < array.length; i++) { bucketsIndex = getBucketIndex(array[i], minValue, significantDigit, radixBase); buckets[bucketsIndex]++; } for (let i = 1; i < radixBase; i++) { buckets[i] += buckets[i - 1]; } for (let i = array.length - 1; i >= 0; i--) { bucketsIndex = getBucketIndex(array[i], minValue, significantDigit, radixBase); aux[--buckets[bucketsIndex]] = array[i]; } for (let i = 0; i < array.length; i++) { array[i] = aux[i]; } return array; };
export function radixSort(array, radixBase = 10) { if (array.length < 2) { return array; }
const minValue = findMinValue(array); const maxValue = findMaxValue(array); let significantDigit = 1;
while ((maxValue - minValue) / significantDigit >= 1) { array = countingSortForRadix(array, radixBase, significantDigit, minValue); significantDigit *= radixBase; } return array; }
|