基数排序(Radix Sort)
基数排序是一种非比较排序算法,旨在对整数或字符串等数据进行排序。基数排序通过将数据按位(或字符)分组并使用稳定的排序算法(如计数排序)对每一位进行排序,从而实现整体的排序。基数排序的时间复杂度为 ,其中 是待排序的元素个数, 是最大位数或字符数。基数排序特别适用于处理大量数据且具有较小的基数(如数字)的场景。
1. 基数排序的基本概念
基数排序的基本思想是:
- 确定排序位数: 找出数据中最长的位数或字符数。
- 从最低位到最高位进行排序: 按照从低到高的顺序,对每一位进行排序。可以使用稳定的排序算法(如计数排序)来实现。
2. 基数排序的算法步骤
以下是基数排序的详细步骤:
-
确定排序位数:
- 找到待排序数据中最长的位数或字符数。
-
按位进行排序:
- 从最低有效位(LSD,Least Significant Digit)开始,对每一位进行排序,直到最高有效位(MSD,Most Significant Digit)。
-
使用稳定的排序算法:
- 对每一位使用稳定的排序算法,如计数排序,来确保相同位的元素保持原有的相对位置。
3. 基数排序的时间复杂度和空间复杂度
-
时间复杂度:
- 最坏情况: ,其中 (n) 是待排序元素的个数, 是最大位数。
- 最佳情况: ,在任何情况下,基数排序的时间复杂度为 。
- 平均情况: ,时间复杂度不受输入数据分布的影响。
-
空间复杂度:
- 空间复杂度: ,需要额外的空间来存储排序结果和计数数组。
4. 基数排序的代码示例
以下是使用 Go 语言实现的基数排序算法:
package main
import "fmt"
// getMax 获取数组中的最大值
func getMax(arr []int) int {
max := arr[0]
for _, v := range arr {
if v > max {
max = v
}
}
return max
}
// countingSort 对数组按照某个位数进行计数排序
func countingSort(arr []int, exp int) {
n := len(arr)
output := make([]int, n) // 输出数组
count := make([]int, 10) // 计数数组
// 统计每个数字出现的次数
for i := 0; i < n; i++ {
index := (arr[i] / exp) % 10
count[index]++
}
// 累加计数数组
for i := 1; i < 10; i++ {
count[i] += count[i-1]
}
// 按位排序
for i := n - 1; i >= 0; i-- {
index := (arr[i] / exp) % 10
output[count[index]-1] = arr[i]
count[index]--
}
// 将排序结果复制到原数组
for i := 0; i < n; i++ {
arr[i] = output[i]
}
}
// RadixSort 对整数切片进行基数排序
func RadixSort(arr []int) {
max := getMax(arr)
// 从最低位到最高位进行排序
for exp := 1; max/exp > 0; exp *= 10 {
countingSort(arr, exp)
}
}
func main() {
arr := []int{170, 45, 75, 90, 802, 24, 2, 66}
fmt.Println("Original array:", arr)
RadixSort(arr)
fmt.Println("Sorted array:", arr)
}
5. 基数排序的优缺点
优点:
- 线性时间复杂度: 基数排序在特定条件下(如数据范围有限)具有线性时间复杂度 。
- 稳定排序: 基数排序是稳定的排序算法,相同值的元素保持原有的相对位置。
- 适用于特定数据: 对于具有较小基数的数据(如整数范围有限的情况),基数排序表现良好。
缺点:
- 空间复杂度高: 基数排序需要额外的空间来存储计数数组和输出数组,空间复杂度为 。
- 受限于数据范围: 基数排序适用于整数或字符排序,不适用于浮点数等复杂数据类型。
- 复杂实现: 相较于简单的排序算法,基数排序的实现较为复杂,需要处理多个位的排序。
总结
基数排序是一种高效的非比较排序算法,特别适用于对整数或字符串等数据进行排序。通过对每一位进行排序,基数排序可以实现高效的排序操作。虽然基数排序在处理大范围数据时可能面临空间复杂度的问题,但其稳定排序和线性时间复杂度的特性使其在处理特定数据时非常有用。