跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

分治算法实战:快速排序与荷兰国旗问题详解

分治算法实战:快速排序与荷兰国旗问题详解。深入探讨三路划分快速排序在数组排序、颜色分类及 TopK 问题中的应用。通过双指针与三指针策略,实现 O(n) 时间复杂度的原地分区,有效处理大量重复元素。对比堆排序与快速选择算法的复杂度差异,提供 C++ 标准库外的底层实现方案。重点解析随机基准值选取对最坏情况的优化,以及递归边界条件的处理,帮助开发者掌握高效排序的核心逻辑与工程实践技巧。

清心发布于 2025/10/23更新于 2026/7/2937 浏览
分治算法实战:快速排序与荷兰国旗问题详解

颜色分类(荷兰国旗问题)

题目描述

给定一个包含 0、1 和 2 的数组,代表红、白、蓝三种颜色。要求在不使用库函数排序的情况下,原地将数组按颜色顺序排列。

核心思路

这道题本质上是三路划分问题。我们可以利用三个指针将数组划分为四个区域:[0, left] 为 0,[left+1, i-1] 为 1,[i, right-1] 为未处理区,[right, n-1] 为 2。

初始化时,left = -1,right = n,i = 0。遍历过程中根据 nums[i] 的值进行不同操作:

  • 遇到 0:交换到左侧区域。执行 swap(nums[++left], nums[i++])。注意这里 i 也要自增,因为交换过来的元素只可能是 1(来自 left+1 位置),无需再次判断。
  • 遇到 1:属于中间区域,直接 i++ 继续向后扫描。
  • 遇到 2:交换到右侧区域。执行 swap(nums[--right], nums[i])。注意此时 i 不能自增,因为从右侧换过来的元素尚未检查,需要留在当前位置重新判断。

当 i 遍历到 right 时,所有元素均已归位。

三路分区示意图

参考实现

class Solution {
public:
    void sortColors(vector<int>& nums) {
        int left = -1, right = nums.size();
        int i = 0;
        while (i < right) {
            if (nums[i] == 0) swap(nums[++left], nums[i++]);
            else if (nums[i] == 1) i++;
            else swap(nums[--right], nums[i]);
        }
    }
};

排序数组

题目描述

给定一个整数数组,不使用内置排序函数,要求时间复杂度为 O(n log n)。这是快速排序的典型应用场景。

核心思路

标准的快速排序通过选取基准值(pivot)将数组分为小于和大于两部分。为了优化性能,我们引入两个关键策略:

  1. 随机基准值:避免在有序或近乎有序数组上退化为 O(n^2)。每次递归前随机选择一个元素作为基准。
  2. 三路划分(Dutch National Flag):将数组分为 < key、= key、> key 三部分。当数组中存在大量重复元素时,可以大幅减少递归深度。

对于三路划分,逻辑与上述颜色分类完全一致,只是比较对象变为当前基准值 key。划分完成后,只需对 < key 和 > key 区间递归,等于 key 的部分已有序,无需再处理。

参考实现

class Solution {
public:
    void qsort(vector<int>& nums, int l, int r) {
        if (l >= r) return;
        // 随机选择基准值
        int key = nums[l + rand() % (r - l + 1)];
        
        // 三路划分
        int left = l - 1, right = r + 1;
        int i = l;
        while (i < right) {
            if (nums[i] < key) swap(nums[++left], nums[i++]);
            else if (nums[i] == key) i++;
            else swap(nums[--right], nums[i]);
        }
        
        // 递归处理左右区间
        qsort(nums, l, left);
        qsort(nums, right, r);
    }

    vector<int> sortArray(vector<int>& nums) {
        srand(time(NULL));
        qsort(nums, 0, nums.size() - 1);
        return nums;
    }
};

第 K 个最大元素

题目描述

找出数组中第 k 个最大的元素。典型的 TopK 问题。

核心思路

解决 TopK 问题通常有两种主流方案:

  1. 堆排序:维护一个大小为 k 的小顶堆,遍历数组后堆顶即为答案。时间复杂度 O(n log k)。
  2. 快速选择(Quick Select):基于快速排序的分区思想。利用三路划分将数组分为 < key、= key、> key 三部分。假设 > key 部分长度为 c,= key 部分长度为 b:
    • 若 c >= k,目标在 > key 区间;
    • 若 b + c >= k,目标就是 key;
    • 否则,目标在 < key 区间,需查找第 k - b - c 大的元素。

虽然标题涉及第 K 大,但以下代码展示了通用的排序框架。在实际工程中,针对'最小 K 个数'这类变体(如库存管理),只需取排序后的前缀即可。该框架的核心在于利用分区特性跳过不必要的递归,平均时间复杂度可达 O(n)。

参考实现

class Solution {
public:
    void qsort(vector<int>& nums, int l, int r) {
        if (l >= r) return;
        int key = nums[l + rand() % (r - l + 1)];
        int left = l - 1, right = r + 1;
        int i = l;
        while (i < right) {
            if (nums[i] < key) swap(nums[++left], nums[i++]);
            else if (nums[i] == key) i++;
            else swap(nums[--right], nums[i]);
        }
        qsort(nums, l, left);
        qsort(nums, right, r);
    }

    vector<int> inventoryManagement(vector<int>& stock, int cnt) {
        srand(time(NULL));
        qsort(stock, 0, stock.size() - 1);
        vector<int> ret(cnt, 0);
        for (int i = 0; i < cnt; i++) ret[i] = stock[i];
        return ret;
    }
};

库存管理 III

题目描述

给定数组 stock 和整数 cnt,返回数组中最小的 cnt 个数。

核心思路

这同样是 TopK 问题的变种。相比全量排序,如果数据量极大,可以使用小顶堆或快速选择来优化。但在本例中,为了展示算法的一致性,我们复用上述快速排序逻辑,排序后直接截取前 cnt 个元素。

实际开发中,若仅需获取结果而不需完整排序,建议优先使用快速选择算法(Quick Select),将时间复杂度从 O(n log n) 降低至 O(n)。

参考实现

class Solution {
public:
    void qsort(vector<int>& nums, int l, int r) {
        if (l >= r) return;
        int key = nums[l + rand() % (r - l + 1)];
        int left = l - 1, right = r + 1;
        int i = l;
        while (i < right) {
            if (nums[i] < key) swap(nums[++left], nums[i++]);
            else if (nums[i] == key) i++;
            else swap(nums[--right], nums[i]);
        }
        qsort(nums, l, left);
        qsort(nums, right, r);
    }

    vector<int> inventoryManagement(vector<int>& stock, int cnt) {
        srand(time(NULL));
        qsort(stock, 0, stock.size() - 1);
        vector<int> ret(cnt);
        for (int i = 0; i < cnt; i++) ret[i] = stock[i];
        return ret;
    }
};

目录

  1. 颜色分类(荷兰国旗问题)
  2. 题目描述
  3. 核心思路
  4. 参考实现
  5. 排序数组
  6. 题目描述
  7. 核心思路
  8. 参考实现
  9. 第 K 个最大元素
  10. 题目描述
  11. 核心思路
  12. 参考实现
  13. 库存管理 III
  14. 题目描述
  15. 核心思路
  16. 参考实现
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 本地部署 Wan2.1 视频生成模型全攻略
  • GitHub 上值得参考的量化交易开源项目
  • MIT 室内场景识别数据集介绍与模型训练实战
  • JDK 27 引入后量子混合密钥交换,应对量子计算威胁
  • 基于 FPGA 的神经网络模型设计与实现:手写数字识别
  • AI 并非前端与 UI 的终结者,而是效率提升的加速器
  • 基于 Python 与 Selenium 的大麦网自动抢票脚本实现
  • 基于 UniApp 与 ThinkPHP 的跨平台应用开发实践
  • ComfyUI 节点式工作流与 AI 图像生成技术指南
  • LLaMA-Factory 命令行工具使用指南
  • LangChain 聊天模型多场景实战:从固定角色到合规客服
  • Git 提交信息规范:Conventional Commits 详解
  • 服务器环境 VS Code GitHub Copilot 加载超时优化与修复
  • Linux 环境下使用 C++ 实现 Shell 基本功能
  • OD 机试真题:Alice 的安全旅行 - 路径最大安全度
  • 具身智能机器人运控通讯架构与实现指南
  • 5 款开源 PPT 生成大模型实测对比:从技术原理到实战效果
  • iOS 新系统兼容适配:UITabBar 液态玻璃效果与 WiFi SSID 获取
  • OpenClaw Cron 系统设计:AI Agent 自主定时任务实现
  • 深度多模态数据融合综述

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online