基数排序算法实现
🔢 基数排序算法实现
1. 算法原理概述
基数排序是一种非比较型的整数排序算法,其核心思想是按照数字的位数逐位排序。它通过将整数按位数切割成不同的数字,然后按每个位数分别比较,从最低位到最高位依次排序,最终使整个序列有序。
核心特性:
- 稳定性:基数排序是稳定排序算法,相同键值的元素相对顺序不会改变。
- 时间复杂度:O(d·(n+k)),其中d是最大数字的位数,n是元素数量,k是基数(通常为10)。
- 空间复杂度:O(n+k),需要额外的桶来存储数据。
2. 算法流程与原理图
2.1 基数排序流程
2.2 排序过程示意图
以数组 [53, 3, 542, 748, 14, 214]
为例:
- 按个位排序:
[542, 53, 3, 14, 214, 748]
- 按十位排序:
[3, 14, 214, 542, 748, 53]
- 按百位排序:
[3, 14, 53, 214, 542, 748]
3. 代码实现
3.1 主入口方法
/* 基数排序 */
public static void RadixSort(IList<int> array, bool ascending, int vectorX = 10, int vectorY = 100)
{// 根据排序方向选择调用升序或降序排序方法if (ascending){RadixSortAscending(array, vectorX, vectorY); // 调用升序排序}else{RadixSortDescending(array, vectorX, vectorY); // 调用降序排序}
}
参数说明:
array
:待排序的整数列表ascending
:排序方向标志(true为升序,false为降序)vectorX
:基数大小,默认为10(对应0-9十个数字)vectorY
:每个桶的容量,默认为100
3.2 升序排序实现
/* 基数排序(升序) */
public static void RadixSortAscending(IList<int> array, int vectorX = 10, int vectorY = 100)
{// 边界条件检查:数组为空或元素数量小于2时无需排序if (array == null || array.Count < 2){return; // 直接返回,不进行任何操作}// 外层循环:遍历每一位数字(个位、十位、百位等)for (int i = 0; i < vectorX; i++){// 创建二维桶数组:vectorX行(0-9),vectorY列(每桶容量)int[,] bucket = new int[vectorX, vectorY];// 将数组中的每个元素分配到对应的桶中foreach (var pivot in array){// 计算当前数字在第i位上的值(从个位开始)var position = (pivot / (int)Math.Pow(10, i)) % 10;// 将数字放入对应桶的第一个空位置for (int l = 0; l < vectorY; l++){if (bucket[position, l] == 0) // 找到桶中的空位置{bucket[position, l] = pivot; // 将数字放入桶中break; // 跳出内层循环,继续处理下一个数字}}}// 从桶中收集数字回原数组(按桶顺序和桶内顺序)for (int o = 0, x = 0; x < vectorX; x++) // o为原数组索引,x为桶编号{for (int y = 0; y < vectorY; y++) // y为桶内位置{if (bucket[x, y] == 0) // 遇到空位置则跳过{continue;}array[o++] = bucket[x, y]; // 将桶中元素放回原数组并更新索引}}}
}
3.3 降序排序实现
/* 基数排序(倒序) */
public static void RadixSortDescending(IList<int> array, int vectorX = 10, int vectorY = 100)
{// 边界条件检查:数组为空或元素数量小于2时无需排序if (array == null || array.Count < 2){return; // 直接返回}// 先执行升序排序RadixSortAscending(array, vectorX, vectorY);// 将升序结果反转得到降序结果for (int i = 0, j = array.Count - 1; i < j; i++, j--){var pivot = array[i]; // 临时存储左侧元素array[i] = array[j]; // 将右侧元素赋给左侧array[j] = pivot; // 将临时存储的左侧元素赋给右侧}
}
4. 算法分析
4.1 位值计算原理
var position = (pivot / (int)Math.Pow(10, i)) % 10;
Math.Pow(10, i)
:计算当前位的权重(1, 10, 100, …)pivot / (int)Math.Pow(10, i)
:将数字右移,使目标位成为个位% 10
:取个位数字,得到0-9的桶索引
4.2 桶排序的应用
- 二维桶结构:
bucket[position, l]
中,position
表示数字位值(0-9),l
表示桶内位置 - 稳定性保证:通过按顺序收集桶中元素,保持相同位值的数字的相对顺序
4.3 性能考虑
- 时间复杂度:循环次数取决于最大数字的位数,与数据规模成线性关系
- 空间开销:使用固定大小的二维数组,可能造成空间浪费但可预知内存使用
5. 算法总结
✅ 优点
- 线性时间复杂度:当位数d较小且数据量n较大时,效率高于比较排序算法
- 稳定性:保持相同键值元素的相对顺序,适用于多关键字排序
- 可预测性能:处理时间与数据分布关系不大,性能稳定
⚠️ 局限性
- 数据类型限制:主要适用于整数或可转换为整数形式的数据
- 空间开销:需要额外的存储空间用于桶结构
- 位数依赖:性能受最大数字位数影响,位数较多时效率下降
6. 应用场景
- 整数排序:特别适合固定长度的整数排序
- 多关键字排序:如按日期(年、月、日)排序
- 字符串排序:可应用于定长字符串的字典序排序
- 大数据量排序:当数据量较大且位数不多时表现优异
此实现通过逐位分配和收集的策略,实现了高效的整数排序,是处理特定类型排序问题的有力工具。