返回
线性排序三剑客:桶排序、基数排序、计数排序
见解分享
2023-09-25 13:50:51
非比寻常的线性排序
与之前讨论过的基于比较的排序算法不同,桶排序、基数排序和计数排序都是基于非比较的,即它们不会比较元素之间的值。这种特性赋予了它们线性时间复杂度 O(n) 的强大优势。
桶排序:分桶装箱,各得其所
想象一下一个装着各种水果的箱子,有苹果、香蕉、橘子。桶排序的做法就是将这些水果按种类分装到不同的桶里,再把桶里的水果按照顺序取出。
基数排序:按位比较,逐层推进
基数排序有点像我们小时候数数,从最低位开始,按位比较,逐层推进。比如要排序一组数字,先比较最右边的个位数,再比较十位数,以此类推,直到最高位。
计数排序:频率统计,按序排列
计数排序的思路很简单,先统计每个元素出现的次数,然后按出现次数的顺序排列元素。它特别适合处理范围有限的整数序列。
实践出真知,代码验证
下面是桶排序、基数排序和计数排序的代码示例:
// 桶排序
def bucketSort(arr: List[Int]): List[Int] = {
val buckets = Array.fill(arr.max + 1)(ListBuffer[Int]())
arr.foreach(num => buckets(num) += num)
buckets.flatten
}
// 基数排序
def radixSort(arr: List[Int]): List[Int] = {
val maxNum = arr.max
var exp = 1
while (maxNum / exp > 0) {
countingSort(arr, exp)
exp *= 10
}
arr
}
// 计数排序
def countingSort(arr: List[Int], exp: Int): Unit = {
val count = Array.fill(10)(0)
arr.foreach(num => count((num / exp) % 10) += 1)
var i = 1
while (i < 10) {
count(i) += count(i - 1)
i += 1
}
val output = ListBuffer[Int]()
i = arr.length - 1
while (i >= 0) {
val index = (arr(i) / exp) % 10
output(count(index) - 1) = arr(i)
count(index) -= 1
i -= 1
}
arr.indices.foreach(i => arr(i) = output(i))
}
结语
桶排序、基数排序和计数排序作为线性排序三剑客,各具特色,在不同的场景下大显身手。它们不仅速度惊人,而且实现起来并不复杂。掌握这些算法,你的排序技能将更上一层楼!