目录
在排序算法的世界里,基数排序犹如一颗独特的明珠。它作为桶排序的一种特例,以其独特的分配和收集过程,为特定场景下的数据排序提供了高效的解决方案。今天,就让我们一同深入探索基数排序的奥秘。
一、基数排序的基本原理
基数排序的核心思想是基于数字的每一位来进行排序。对于十进制数,每一位的取值范围是 0~9,我们可以据此建立 10 个桶。排序过程从最低位开始,依次对每一位数字进行分配和收集操作,直到最高位排序完成。
1. 分配过程
以一组数为例,首先将这些数按个位数字的值分配到相应的桶中。例如,个位为 3 的数就放入 3 号桶,个位为 5 的数放入 5 号桶,以此类推。这个过程中,并不对桶内的数进行排序,只是简单地将它们放入对应的桶中。
2. 收集过程
分配完成后,按照桶的编号顺序(从 0 号桶到 9 号桶)依次取出桶中的数,重新组合成一个新的数列。此时,新数列中的数在个位上是有序的。接着,再以十位数字为依据进行同样的分配和收集操作,如此重复,直到最高位排序结束。
二、Java 代码实现基数排序
1. 整体思路
在 Java 中实现基数排序,我们首先需要确定数组中的最大值,从而得知数字的最大位数,这将决定循环的次数。然后,针对每一位数字,将数组中的元素分配到相应的桶中,再从桶中收集元素放回原数组,同时清空桶,为下一位数字的排序做准备。
2. 关键代码解析
import java.util.ArrayList;
import java.util.List;
public class RadixSort {
// 基数排序方法
public static void radixSort(int[] array) {
// 找到数组中的最大值
int max = findMax(array);
// 计算最大值的位数
int maxDigits = getMaxDigits(max);
// 针对每一位数字进行排序
for (int i = 1; i <= maxDigits; i++) {
// 创建10个桶,每个桶是一个ArrayList
List<Integer>[] buckets = new ArrayList[10];
for (int j = 0; j < 10; j++) {
buckets[j] = new ArrayList<>();
}
// 将数组中的元素分配到相应的桶中
for (int num : array) {
int digit = getDigit(num, i);
buckets[digit].add(num);
}
// 从桶中收集元素放回原数组
int index = 0;
for (List<Integer> bucket : buckets) {
for (int num : bucket) {
array[index++] = num;
}
bucket.clear(); // 清空桶,准备下一次排序
}
}
}
// 找到数组中的最大值
private static int findMax(int[] array) {
int max = array[0];
for (int num : array) {
if (num > max) {
max = num;
}
}
return max;
}
// 计算数字的位数
private static int getMaxDigits(int num) {
int digits = 0;
while (num!= 0) {
num /= 10;
digits++;
}
return digits;
}
// 获取数字指定位置上的数字
private static int getDigit(int num, int position) {
for (int i = 1; i < position; i++) {
num /= 10;
}
return num % 10;
}
public static void main(String[] args) {
int[] array = {170, 45, 75, 90, 802, 24, 2, 66};
radixSort(array);
for (int num : array) {
System.out.print(num + " ");
}
}
}
3. 代码逻辑剖析
在radixSort
方法中,首先通过findMax
方法找到数组中的最大值,再由getMaxDigits
方法计算出最大值的位数,从而确定循环次数。在每次循环中,创建 10 个桶,然后根据当前要排序的位数(从个位开始),通过getDigit
方法获取每个数在该位上的数字,将其分配到对应的桶中。分配完成后,从桶中依次取出元素放回原数组,并清空桶。重复这个过程,直到所有位数都排序完成。
三、基数排序的特点与应用场景
1. 时间复杂度
基数排序的时间复杂度为,其中是最大数字的位数,是数字的个数。这意味着,当数字的位数相对较小时,基数排序能够在接近线性的时间内完成排序。
2. 与普通桶排序的区别
- 桶的个数固定:基数排序的桶个数固定为 10(对于十进制数),而普通桶排序的桶个数可能根据数据范围和分布动态调整。
- 入桶方式独特:基数排序根据数字的每一位作为桶的编号进行入桶,而普通桶排序通常使用数值乘以一个系数再除以最大值加一的方式确定桶编号。
- 循环结构不同:基数排序的入桶出桶操作包含在一个与最大数值位数相关的循环中,普通桶排序的循环结构可能更复杂,与数据分布等因素相关。
3. 应用场景与限制
- 适用场景:适用于小范围数据的排序,因为这样可以较好地控制辅助数组(桶)的长度。例如,对手机号码的后几位进行排序,或者对年龄等范围有限的数据进行排序。
- 限制条件:只能应用于十进制数据,对于字符串或其他非十进制数据,需要进行额外的处理。同时,当数据分布不均匀,集中在某几个桶时,性能可能会受到影响。另外,处理负数时需要额外的转换操作,增加了一定的复杂性。
基数排序以其独特的排序方式,在特定的数据场景下展现出了高效性。但正如每一种算法都有其适用范围一样,我们需要根据实际情况选择合适的排序算法。希望通过这篇文章,您对基数排序有了更深入的理解,能够在编程实践中灵活运用。