冒泡排序是一种基础排序算法。它通过相邻元素两两比较,把较大的元素逐步交换到数组末尾。实际项目里很少直接使用冒泡排序处理大数据量,但它适合用来理解排序的基本思想、稳定性、时间复杂度和提前终止优化。
整体流程
基础实现
假设有一组仓库拣货任务,需要按优先级从小到大排序:
1 | int[] priorities = {8, 3, 2, 6, 7, 9}; |
冒泡排序实现:
1 | public void bubbleSort(int[] arr) { |
每一轮结束后,当前未排序区间中的最大值会被交换到末尾。因此内层循环的边界是 arr.length - i - 1。
提前终止优化
如果某一轮没有发生交换,说明数组已经有序,可以提前结束:
1 | public void bubbleSortWithBreak(int[] arr) { |
这个优化对“基本有序”的数据有明显收益。例如仓库任务列表已经按创建时间大致排好,只是少量紧急任务插入,提前终止可以减少无效比较。
边界条件与测试
排序算法最容易出错的地方不是核心循环,而是边界条件。至少要覆盖空数组、单元素数组、已经有序、完全逆序、存在重复值这几类输入:
1 | public static void main(String[] args) { |
在供应链任务排序里,重复值尤其常见。比如两个拣货任务优先级相同,或者两个订单承诺发货时间一致。冒泡排序在只使用 > 交换时是稳定的,相同优先级任务不会改变原有顺序。这一点可以帮助我们理解“稳定排序”为什么对业务有意义:同优先级时保留创建顺序,能减少人工理解成本。
供应链业务例子
假设 WMS 中有少量待处理任务,需要在内存中按优先级排序:
1 | public class PickTask { |
如果任务数量只有十几个,冒泡排序可以作为教学示例理解排序过程。但在真实系统中,不建议自己手写冒泡排序处理任务列表,应该优先使用 JDK 提供的排序:
1 | tasks.sort(Comparator |
业务代码更应该关注排序规则是否正确:优先级、创建时间、仓库、波次、客户等级等字段的优先顺序。
生产系统里怎么选择排序方式
生产系统通常不应该把大量数据查到 JVM 里再排序。供应链系统的数据规模很容易放大:订单可能是几十万级,库存流水可能是千万级,仓库任务在促销期间也会快速增长。
更常见的选择是:
- 数据库排序:适合按索引字段分页查询,例如
created_at、priority、status。 - 搜索引擎排序:适合复杂筛选和全文检索,例如订单号、客户名称、商品名称组合查询。
- JDK 内置排序:适合已经筛选出的小批量候选集,例如 100 条待分配任务做二次排序。
- 手写排序:主要用于学习、面试和解释算法过程,不建议作为业务主实现。
也就是说,冒泡排序的工程价值在于训练基本功,而不是替代成熟排序能力。真正写业务代码时,要先判断数据量、排序字段、分页方式和索引条件。
复杂度和稳定性
冒泡排序特点:
- 最好时间复杂度:
O(n),前提是加了提前终止且数据已经有序。 - 平均和最坏时间复杂度:
O(n^2)。 - 空间复杂度:
O(1)。 - 稳定性:稳定。相等元素不会因为
>比较而交换相对顺序。
稳定性在业务里有意义。例如两个拣货任务优先级相同,稳定排序可以保留原来的创建顺序。
小结
冒泡排序适合理解排序思想,不适合大规模生产数据。供应链系统里的订单、库存、任务、流水通常数量很大,生产代码应该使用数据库排序、索引排序或 JDK 内置排序。学习冒泡排序的重点,是理解相邻比较、边界缩小、提前终止和稳定性。