基数排序

148 阅读2分钟

「这是我参与2022首次更文挑战的第16天,活动详情查看:2022首次更文挑战

在说基数排序之前,先简单记录一下计数排序。计数排序的确不同于桶排。

计数排序

计数排序适合整数数值排序,且值域范围有限。 比如给高考考生的得分进行排序,值域范围是[0, 750]十分有限。

流程是先统计每个数值对应的元素的次数,把相同数值的元素划分到一个区间然后计算每个区间的开始下标,通过求前缀和的方式计算下标,按这个顺序 , 给它放回去,就排序完成了。三轮迭代,时间复杂度O(n).

image.png

基数排序

基数排序是具有相对稳定性的排序。

假设,我们现在排序的目标是一个只包含两位数的数组。 我们用基数排序的大概步骤是这样的。

1 对每个元素的个位进行计数排序

2 对每个元素的十位进行计数排序

假设,数组本来是这样的[ 81, 39, 4, 85, 17, 14, 83, 80 ]

我们进行第一轮个位排序, [80, 81, 83, 4,14, 85, 17, 39 ]

从这个结果上就能看出 稳定性是什么。 4之所以排在14前面,不是因为4比14小, 而是因为在原数组中4 就在14的前面。 个位排序之后,不同个位的数肯定不在一块,而个位相同的数的相对顺序还是保持排序之前的顺序

我们再进行第二轮十位数排序, [4, 14, 17 ,39 , 80 , 81, 83, 85 ].

排序就完成了。 对于二位数,他们的十位和个位都是升序,那么他们本身肯定就是升序。

高位和低位

上面的做法只适用十进制的两位数, 如果三位数,难懂要三排,五位数五排?。

当然不是, 我们统一把小于2的32次的数,当做是2^16(65536)进制, 这样这些数就都只有两位, 高16位和低16位。 所以现在的问题就是如何取得高十六位和低十六位,简单的做法就是取模

但是有更好的做法就是位与运算,因为我们限定了范围和进制正好就是2的16次, 位与运算的结果就是只有两者都是1才是1,这样取低16位就只需要和 65536-1 进行位与运算 , 取高16位则需要 和 0xffff0000 进行位与运算 ,然后右移 16 。 注意 位与运算的优先级高于位移运算。

image.png

附上leetcode 164. 最大间距