快速排序是算法领域的"九阳神功",掌握其精髓能让你在算法修炼之路上突破瓶颈。
1. 快速排序的核心思想
快速排序(Quicksort)是一种基于分治思想的高效排序算法,核心步骤为:
- 选择基准值(Pivot):通常选择待排序区间的第一个元素(也可随机选或取中间值)。
[元素≤pivot] + [pivot] + [元素>pivot]
- Partition(分割)操作:
- 将数组分为两部分,使得基准值左侧元素均 ≤ 基准值,右侧元素均 ≥ 基准值。
- 实现方法:通过交替从右向左、从左向右扫描,交换不符合条件的元素,最终确定基准值的正确位置。
- 递归处理子数组:对基准值左右两侧的子数组重复上述步骤,直到所有元素有序。
2. Partition操作的关键细节
- 空位交替填充:选择基准值后,其初始位置视为“空位”,通过交替从后向前扫描小元素、从前向后扫描大元素,将元素填入空位,最终确定基准值的位置。
- 时间复杂度:单次 Partition 操作的时间复杂度为 O(n),需遍历整个子数组。
示例:
原始数组 [8, 6, 3, 10, 9, 7, 2, 12]
,选择 8
为基准值:
- 从右向左找到
2 < 8
,将其填入空位 →[2, 6, 3, 10, 9, 7, _, 12]
。 - 从左向右找到
10 > 8
,将其填入空位 →[2, 6, 3, _, 9, 7, 10, 12]
。 - 重复直至基准值位置确定 →
[2, 6, 3, 7, 9, 8, 10, 12]
。
3.两种经典实现方式
1. Lomuto分区(直观易实现):
- 以最后一个元素为基准
- 双指针维护分割点
def partition(arr, low, high):pivot = arr[high]i = low # 分割点for j in range(low, high):if arr[j] <= pivot:arr[i], arr[j] = arr[j], arr[i]i += 1arr[i], arr[high] = arr[high], arr[i]return i
2. Hoare分区(更高效):
- 使用首元素为基准
- 双指针从两端向中间扫描
def partition(arr, low, high):pivot = arr[low]left, right = low+1, highwhile True:while left <= right and arr[left] <= pivot: left += 1while left <= right and arr[right] >= pivot: right -= 1if left > right: breakarr[left], arr[right] = arr[right], arr[left]arr[low], arr[right] = arr[right], arr[low]return right
4. 时间复杂度分析
快速排序的性能高度依赖基准值的选择:
- 最好情况:每次基准值均接近中位数,递归树高度为 log₂n,时间复杂度为 O(n log n)。
- 最坏情况:基准值始终为极值(如最大值或最小值),递归退化为链表,时间复杂度为 O(n²)。
- 平均情况:随机选择基准值时,时间复杂度接近 O(n log n)。
类比二叉树:
- 每次 Partition 将数组分为左右子树,递归深度对应树的高度。
- 树越平衡(层数少),效率越高;树越不平衡(层数多),效率越低。
5. 快速排序的优化与应用
- 工程优化:混合排序策略(如 C++ STL 的
sort
函数结合快速排序、插入排序和堆排序)。 - 尾递归优化:
def quick_sort(arr, low, high):while low < high:pi = partition(arr, low, high)quick_sort(arr, low, pi-1)low = pi + 1 # 减少递归深度
- 三路快排:
- 处理大量重复元素:划分为 <pivot、=pivot、>pivot 三部分
- 实际应用:
- 2-sum 问题:先排序再使用双指针法高效求解。
- 快速选择算法(QuickSelect):基于 Partition 操作,在 O(n) 时间内找到第 k 小元素(下节课重点)。
6. 学习要点与误区
- 关键思维:理解 Partition 操作是掌握快速排序的核心。
- 常见误区:
- 忽视基准值选择对性能的影响。
- 混淆 Partition 实现细节(如扫描顺序、交换条件)。
- 实践建议:
- 手动模拟 Partition 过程(如纸上画图)。
- 尝试编码实现快速排序,并通过调试理解递归流程。
7.总结
快速排序通过“分治 + Partition”高效解决排序问题,其核心在于:
- 分治思想:将大问题分解为小问题递归处理。
- 基准值选择:直接影响算法性能,需结合实际场景优化。
- 时间复杂度:平均 O(n log n),是多数场景下的首选排序算法。
掌握快速排序不仅为后续算法(如快速选择、归并排序)奠定基础,更是理解分治与递归思维的经典案例。