前言
基数排序(Radix Sort)是一种非比较性的整数排序算法,它根据数字的每个位上的值进行排序。它将待排序的元素按照低位到高位的顺序依次进行排序,直到最高位。
算法实现
- 基本步骤:
- 获取待排序列中的最大值,并确定它的位数。
- 从最低位开始,使用计数排序按照当前位数的值对待排序序列进行稳定的排序。
- 继续对下一位进行排序,直到对最高位排序完成。
- 完成排序后,待排序序列就变为有序序列。
基数排序的C语言实现:
/// <summary>
/// 按当前位计数排序
/// </summary>
/// <param name="arr">待排序数组</param>
/// <param name="arrsize">待排序数组大小</param>
/// <param name="exp">当前位的指数</param>
void CountSort(int arr[], int arrsize, int exp) {
int* output = malloc(sizeof(int) * arrsize);//储存排序结果的辅助数组
int count[10] = { 0 };//储存各个数字出现次数
int i, t;
//统计当前位每个数字出现次数
for (i = 0; i < arrsize; i++) {
count[(arr[i] / exp) % 10]++;
}
//计算每个数字在输出数组output中的位置
for (i = 1; i < 10; i++) {
count[i] += count[i - 1];
}
//构建排序后的输出数组
for (i = arrsize - 1; i >= 0; i--) {
t = (arr[i] / exp) % 10;//计算当前元素位上的数
output[count[t] - 1] = arr[i];//将当前元素arr[i]放到输出数组中正确位置
count[t]--;//对应位置的计数减1,表示该位置已经用了一个元素
}
//将排序结果拷贝到原数组
for (i = 0; i < arrsize; i++) {
arr[i] = output[i];
}
free(output);//释放辅助数组空间
}
/// <summary>
/// 基数排序
/// </summary>
/// <param name="arr">待排序数组</param>
/// <param name="arrsize">待排序数组大小</param>
void RadixSort(int arr[], int arrsize) {
int max = getmax(arr, arrsize);//获取数组中最大数
//对每个位数进行计数排序
for (int exp = 1; max / exp > 0; exp *= 10) {
CountSort(arr, arrsize, exp);
}
}
基数排序的核心思想是将元素按照位数进行排序,从低位到高位逐个进行稳定的排序操作。在每个位数上的排序过程中,可以使用稳定的排序算法,如计数排序或桶排序。
在计数排序中比较难理解的地方是这段代码:
//计算每个数字在输出数组output中的位置
for (i = 1; i < 10; i++) {
count[i] += count[i - 1];
}
//构建排序后的输出数组
for (i = arrsize - 1; i >= 0; i--) {
t = (arr[i] / exp) % 10;//计算当前元素位上的数
output[count[t] - 1] = arr[i];//将当前元素arr[i]放到输出数组中正确位置
count[t]--;//对应位置的计数减1,表示该位置已经用了一个元素
}
如何实现将arr[i]放到正确位置的呢?
不妨在运行中打印数组count,对数组arr元素个位排序:
计算位置后 count[i]的值为当前位上的数在排序数组中的正确位置,count[t]-1为arr[i]在输出数组output中的正确位置,因为下标是从0开始,所以要减1,当前位上的数计数可能不为1,如7有3个,所以在排了一个数后,该数对应的count计数要减1,那么下次个位为7的数就会排到它的后面,重复该操作就能将所有数放到正确位置。
总结
基数排序的时间复杂度取决于元素的位数和基数的大小。如果待排序序列有 n 个元素,d 代表最大元素的位数,k 表示基数的大小,则基数排序的时间复杂度为 O(d*(n+k))。
基数排序通常适用于待排序元素的位数不是很大的情况下,且待排序元素需要满足一定的条件,例如整数类型。它相对于比较性排序算法,可以在某些情况下具有更好的性能。