返回

线性排序三剑客:桶排序、基数排序、计数排序

见解分享

非比寻常的线性排序

与之前讨论过的基于比较的排序算法不同,桶排序、基数排序和计数排序都是基于非比较的,即它们不会比较元素之间的值。这种特性赋予了它们线性时间复杂度 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))
}

结语

桶排序、基数排序和计数排序作为线性排序三剑客,各具特色,在不同的场景下大显身手。它们不仅速度惊人,而且实现起来并不复杂。掌握这些算法,你的排序技能将更上一层楼!