1. 从零理解快速排序与颜色分类问题
作为一名长期刷题的算法工程师,我经常遇到类似颜色分类这样的基础排序问题。这道题看似简单,但能很好地考察对排序算法的理解深度。我们先从问题本身出发,逐步拆解快速排序的实现原理。
1.1 问题本质与约束条件
题目要求对只包含0、1、2的数组进行原地排序,这意味着:
- 不能使用额外空间(空间复杂度O(1))
- 必须直接修改原数组
- 不能调用语言内置的sort函数
在实际工程中,这种限制很常见。比如在嵌入式系统中,内存资源有限,原地算法能节省宝贵的内存空间。又如在某些安全场景下,直接修改原数据比创建副本更符合要求。
1.2 快速排序的核心思想
快速排序采用分治策略,其核心操作是partition(分区):
- 选择一个基准值(pivot)
- 将小于基准的元素移到基准左侧
- 将大于基准的元素移到基准右侧
- 对左右子数组递归执行上述操作
这种分而治之的思想在算法设计中非常普遍。就像整理书架时,我们可以先按书籍类型大致分区,再对每个区域单独排序,效率远高于一次性整理全部书籍。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 代码实现深度解析
让我们逐行分析提供的快速排序实现,理解每个细节的设计考量。
2.1 基准选择策略
cpp复制int flag = a[(left + right) / 2]; // 选取中间值作为基准
选择中间索引作为基准是平衡性较好的策略:
- 时间复杂度:O(1)的随机访问
- 空间复杂度:仅存储一个整数值
- 避免极端情况:相比总是选择第一个元素,能减少已排序数组的最坏情况
提示:在实际工程中,更稳健的做法是"三数取中法"——取左端、中间、右端三个元素的中值作为基准,进一步避免最坏情况。
2.2 分区过程详解
cpp复制do {
while (a[i] < flag) i++; // 从左找≥基准的元素
while (a[j] > flag) j--; // 从右找≤基准的元素
if (i <= j) {
swap(a[i], a[j]); // 交换元素
i++;
j--;
}
} while (i <= j);
这个do-while循
