C++ 快速排序(Quick Sort)深度精讲:分治思想、Lomuto 分区法及三数取中优化,面试手撕必会

一、算法核心原理("是什么"与"为什么快")

快速排序之所以"快",在于它的分区操作能在一次遍历中将一个元素(基准)放到最终位置上,同时让左右两侧满足大小关系。

相比于希尔排序的"逐渐逼近",快排采用的是递归分割策略------每次都将问题规模减半,因此平均深度仅为 log₂n。

二、经典 C++ 实现:Lomuto 分区法(最易理解的写法)

这是面试中最推荐手撕的版本,逻辑直观,不易出错。核心是维护一个 i 指针,指向"小于等于基准的区域的末尾"。

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

// 分区函数:将 arr[low..high] 分区,返回基准元素的最终下标
int partition(vector<int>& arr, int low, int high) {
    int pivot = arr[high]; // 选最右边的元素作为基准
    int i = low - 1;       // i 指向小于等于 pivot 的区域的最后一个位置

    for (int j = low; j < high; j++) {
        // 当前元素小于等于基准时,将其与 i+1 位置交换,扩大小于区域
        if (arr[j] <= pivot) {
            i++;
            swap(arr[i], arr[j]);
        }
    }
    // 最后将基准放到中间位置(i+1)
    swap(arr[i + 1], arr[high]);
    return i + 1; // 返回基准的最终位置
}

// 快速排序递归主函数
void quickSort(vector<int>& arr, int low, int high) {
    if (low < high) {
        int pi = partition(arr, low, high); // pi 是基准的索引
        quickSort(arr, low, pi - 1);        // 递归排左边
        quickSort(arr, pi + 1, high);       // 递归排右边
    }
}

三、核心优劣分析(面试必答)

  • 优势 1:极佳的缓存局部性。分区操作是在原数组上顺序遍历,对 CPU 缓存极其友好,常数因子远小于归并排序和堆排序。

  • 优势 2:原地排序(In-place)。除了递归栈开销,不需要额外的大块内存(不像归并需要 O(n) 辅助空间)。

  • 致命短板(最坏情况) :当数组已经有序 (正序或逆序)且每次选最后一个元素作基准时,分区极度不平衡,递归深度变为 n,时间复杂度退化为 O(n²)

四、工程级优化策略(加分项,展示深度)

为了防止最坏情况,C++ 的 std::sort(内省排序)混用了快排并加入了保险机制。面试手撕时,你可以主动提出以下几种优化:

  1. 三数取中(Median-of-Three) :不选固定端,而是取 lowmidhigh 三个位置的中位数作为基准,极大降低遇到有序数组时退化的概率。
cpp 复制代码
int mid = low + (high - low) / 2;
if (arr[mid] < arr[low]) swap(arr[mid], arr[low]);
if (arr[high] < arr[low]) swap(arr[high], arr[low]);
if (arr[high] < arr[mid]) swap(arr[high], arr[mid]);
swap(arr[mid], arr[high]); // 将中位数放到 high 位置作为 pivot
  1. 小区间插入排序:当子数组长度小于阈值(如 10~15)时,递归开销过大,直接改用插入排序。
  2. 随机化基准int pivot = arr[low + rand() % (high - low + 1)]; 从概率上规避最坏情况。

五、总结速查

  • 手撕必用:Lomuto 分区法(代码短)。

  • 面试亮点:主动说出"快排是不稳定的"、"有序数组会让它变慢,需要三数取中优化"。

  • 工程警告 :写业务代码永远不要手写快排,请直接调用 std::sort,因为它是内省排序,已经内置了上述所有优化。

资源推荐

《C++八股文》2026版

贪心算法

相关推荐
WWJA王文举13 小时前
I²C通信完整流程详解:START、地址、ACK、数据、Repeated START和STOP一次讲透
c语言·开发语言
zhanghaha131414 小时前
Python进阶教程:6_JSON 数据解析 —— 新手完全指南
开发语言·python·json
码匠许师傅15 小时前
【C++ 面试真题】聊聊 C++ 的拷贝构造与拷贝赋值
java·c++·面试
qq_4480111615 小时前
C语言中的动态内存分配
c语言·开发语言·php
杨航 AI15 小时前
O(n log n):线性对数原理拆解 这个是排序算法的黄金复杂度之一。
算法·排序算法
动词ing16 小时前
【学习笔记】C语言(数组指针与指针数组+字符数组+函数+参数传递+字符串作为形参+递归函数+指针函数+回调函数+结构体嵌套+内存动态分配函数)
c语言·笔记·学习
旖旎夜光16 小时前
LeetCode 3:无重复字符的最长子串(滑动窗口) —— 题解
数据结构·c++·算法·leetcode·滑动窗口
汉字萌萌哒16 小时前
2024CSP-J入门级C++真题详解
开发语言·c++
arbboter16 小时前
【网络工具】NetProxy网络代理用户手册
开发语言·网络·c#·网络代理·网络异常·代理工具
sakiko_16 小时前
OC学习笔记-面试题
面试·objective-c