计数排序算法详解 - 从原理到实现

计数排序算法详解 - 从原理到实现

LeetCode-Py ⛽️「算法通关手册」:超详细的「算法与数据结构」基础讲解教程,从零基础开始学习算法知识,800+ 道「LeetCode 题目」详细解析,200 道「大厂面试热门题目」。 LeetCode-Py 项目地址: https://gitcode.com/gh_mirrors/le/LeetCode-Py

计数排序概述

计数排序是一种非比较型的线性时间排序算法,特别适用于整数数据的排序场景。与常见的基于比较的排序算法(如快速排序、归并排序)不同,计数排序通过统计元素出现次数来实现排序,这使得它在特定条件下可以达到O(n)的时间复杂度。

算法核心思想

计数排序的基本原理可以概括为以下三个步骤:

  1. 统计频次:遍历数组,统计每个元素出现的次数
  2. 计算位置:根据统计结果计算每个元素在排序后数组中的最终位置
  3. 重构数组:按照计算得到的位置信息,将元素放置到正确位置

这种思想充分利用了数组索引的有序性,通过将元素值映射到数组索引来实现排序。

算法详细步骤

1. 确定数据范围

首先需要确定待排序数组中元素的最小值nums_min和最大值nums_max,计算出数据范围:

size = nums_max - nums_min + 1

这个范围决定了我们需要多大的计数数组来统计所有可能的元素值。

2. 初始化计数数组

创建一个大小为size的计数数组counts,初始化为全0:

counts = [0 for _ in range(size)]

3. 统计元素频次

遍历原始数组,统计每个元素出现的次数:

for num in nums:
    counts[num - nums_min] += 1

这里使用num - nums_min作为索引,将元素值映射到从0开始的连续索引。

4. 计算累积频次

将计数数组转换为累积计数数组,表示每个元素在排序后数组中的最后位置:

for i in range(1, size):
    counts[i] += counts[i - 1]

5. 构建排序结果

逆序遍历原始数组,根据累积计数数组将元素放置到正确位置:

res = [0 for _ in range(len(nums))]
for i in range(len(nums) - 1, -1, -1):
    num = nums[i]
    res[counts[num - nums_min] - 1] = num
    counts[num - nums_min] -= 1

逆序遍历保证了排序的稳定性,即相同元素的相对顺序保持不变。

算法特性分析

时间复杂度

计数排序的时间复杂度为O(n + k),其中:

  • n是待排序数组的长度
  • k是数据的范围大小(最大值与最小值的差加1)

当k与n同数量级时,时间复杂度可视为线性O(n)。

空间复杂度

计数排序需要额外的O(k)空间来存储计数数组。当数据范围很大时,这会消耗较多内存。

稳定性

计数排序是一种稳定的排序算法,因为逆序遍历和放置保证了相同元素的原始相对顺序。

适用场景

计数排序最适合以下场景:

  1. 待排序数据为整数
  2. 数据范围不大(k值较小)
  3. 需要稳定排序
  4. 数据分布相对集中

实际应用示例

假设我们有数组[3, 0, 4, 2, 5, 1, 3, 1, 4, 5],让我们看看计数排序如何处理它:

  1. 确定范围:最小值为0,最大值为5,范围size=6
  2. 统计频次:counts = [1, 2, 1, 2, 2, 2]
  3. 累积计数:counts = [1, 3, 4, 6, 8, 10]
  4. 逆序填充:
    • 最后一个元素5:放在位置9(counts[5]=10-1=9)
    • 倒数第二个元素4:放在位置7(counts[4]=8-1=7)
    • ...依此类推

最终得到排序结果[0, 1, 1, 2, 3, 3, 4, 4, 5, 5]

算法优化与变种

简化版本

对于不需要稳定排序的场景,可以省略累积计数步骤,直接根据频次重建数组:

res = []
for num in range(nums_min, nums_max + 1):
    res.extend([num] * counts[num - nums_min])

这种实现更简单,但失去了稳定性。

处理负数

基本实现已经可以处理负数,因为nums_min可能是负数,计算索引时会自动调整。

大范围数据

对于数据范围很大的情况,可以考虑以下优化:

  1. 分段处理:将大范围分成多个小范围分别排序
  2. 基数排序:结合计数排序实现多轮排序

总结

计数排序是一种高效的线性时间排序算法,特别适合整数排序且数据范围不大的场景。它的主要优势在于时间复杂度低且稳定,但需要额外的空间开销。理解计数排序的原理有助于我们更好地掌握非比较排序的思想,也为学习更复杂的基数排序等算法打下基础。

在实际应用中,当数据满足计数排序的前提条件时,它可以提供比传统比较排序更好的性能表现。

LeetCode-Py ⛽️「算法通关手册」:超详细的「算法与数据结构」基础讲解教程,从零基础开始学习算法知识,800+ 道「LeetCode 题目」详细解析,200 道「大厂面试热门题目」。 LeetCode-Py 项目地址: https://gitcode.com/gh_mirrors/le/LeetCode-Py

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尤瑾竹Emery

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值