一 概述
Lomuto 分区方案是快速排序算法中常用的一种分区策略,由 Nico Lomuto 提出。它的核心思想是通过一次遍历将数组划分为两个子数组,使得左子数组的元素均小于等于基准值(pivot),右子数组的元素均大于基准值。
二 Lomuto 分区的步骤
1 选择基准值(pivot)
通常选择当前子数组的最后一个元素作为基准值(也可优化为随机选择)。
2 初始化指针
定义指针 i,初始指向子数组的起始位置 low - 1。i的作用是标记小于等于基准值的子数组的末尾。
3 遍历数组
使用指针 j从左到右遍历子数组 [low, high-1],逐个比较元素与基准值:
若 arr[j] ≤ pivot,则 i 自增 1,并交换 arr[i] 和 arr[j]。
若 arr[j] > pivot,直接跳过。
4 放置基准值:
遍历完成后,将基准值 arr[high] 交换到 i+1 的位置。此时:
[low, i]的元素均 ≤ pivot,
[i+2, high]的元素均 > pivot,
i+1是基准值的最终位置。
二 代码实现
#include <iostream>
using namespace std;
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
int partition(int arr[], int low, int high) {
int pivot = arr[high]; // 选择最后一个元素作为基准
int i = low - 1; // 指向比基准小的元素的末尾
for (int j = low; j <= high - 1; j++) {
if (arr[j] < pivot) {
i++;
swap(arr[i], arr[j]);
}
}
swap(arr[i + 1], arr[high]); // 将基准放到正确位置
return i + 1;
}
三 示例演示
假设数组为 [3, 7, 2, 5, 1, 4],选择最后一个元素 4作为基准:
遍历过程:
j=0, 元素 3 ≤ 4→ i=0, 交换 arr[0] 和自身 → 数组不变。
j=1, 元素 7 > 4 → 无操作。
j=2, 元素 2 ≤ 4→ i=1, 交换 arr[1]和 arr[2] → 数组变为 [3, 2, 7, 5, 1, 4]。
j=3, 元素 5 > 4 → 无操作。
j=4, 元素 1 ≤ 4 → i=2, 交换 arr[2]和 arr[4] → 数组变为 [3, 2, 1, 5, 7, 4]。
放置基准值:交换 i+1=3和 high=5→ 数组变为 [3, 2, 1, 4, 7, 5]。
最终基准值 4位于索引 3,左子数组 [3, 2, 1]≤ 4,右子数组 [7, 5]> 4。
四 时间复杂度
平均情况:(O(n)),单次分区需遍历整个子数组。
五 优缺点
1 优点
实现简单,逻辑清晰。
适合教学和小规模数据。
2 缺点
对已有序数组效率低下(需优化基准选择)。
重复元素较多时性能较差(Hoare 分区更高效)。
六 优化建议
1 随机化基准
随机选择 pivot而非固定最后一个元素,减少最坏情况概率。
2 三数取中法
取子数组首、中、尾三元素的中位数作为基准,进一步平衡分区。
七 与 Hoare 分区的对比
特性 Lomuto 分区 Hoare 分区
实现复杂度 简单 较复杂
交换次数 较多(每次交换需移动元素) 较少
重复元素处理 效率低 更高效
基准位置 固定为子数组末尾 初始位置或中间
Lomuto 分区是理解快速排序的基础,但在实际应用中,结合随机化基准或 Hoare 分区能显著提升性能。