前言LeetCode 第 769 题「最多能完成排序的块」给一个长度为 n 的数组它是0到n-1的一个排列。要求把数组切成尽量多的连续块对每块单独排序后按原顺序拼接起来结果等于整个数组升序排列。题目的关键约束是「排列」每个值恰好出现一次取值范围正好是0..n-1。这个约束让问题有一个非常简洁的 O(n) 解法——维护前缀最大值当最大值等于当前下标时就可以在这里切一刀。初学者容易把它做成「对每个前缀排序再比较」复杂度 O(n² log n)虽然能过小数据但完全没抓住排列的性质。另一个误区是把 769 的贪心直接套到 768允许重复值上那里前缀最大值等于下标的条件不再成立。本文讲贪心的正确性、边界条件以及一个适用于重复值版本的单调栈解法最后列出常见坑点。一、问题转化把一个块切在位置i之后即前i1个元素为一块需要满足什么条件由于整个数组是0..n-1的排列前i1个位置最终应该放的就是0..i这i1个数。如果前i1个元素恰好就是集合{0, 1, ..., i}那么它们单独排序后正好占据排序结果的前i1个位置这一刀就是合法的。而「前i1个元素恰好是0..i」等价于前i1个元素的最大值等于i。理由如果最大值等于i由于所有元素互不相同且都非负这i1个元素必然是0..i的某个排列。如果最大值大于i说明有一个本该放在后面的数跑到了前面这一刀切下去前一块排序后无法与后一块衔接。所以算法就是从左到右扫描维护当前前缀最大值curMax每当curMax i时块数加一。二、贪心实现// 适用于 C17#include algorithm#include vectorint maxChunksToSorted(const std::vectorint arr) {int chunks 0;int curMax -1;for (int i 0; i static_castint(arr.size()); i) {curMax std::max(curMax, arr[i]);if (curMax i) {chunks;}}return chunks;}几个细节curMax初始化为-1保证第一个元素最小为 0一定能更新它。如果初始化成 0遇到arr[0] 0时也正确但如果以后把函数改成处理其他范围-1更稳妥。循环变量用int避免size_t与int比较时的符号警告。arr.size()先转成int前提是长度不超过INT_MAX这在题目约束内成立。条件必须是curMax i不能写成。虽然在排列的前提下curMax i恒成立前缀最大值至少是i但才表达「恰好填满」的语义。如果输入不满足排列约束会给出错误答案。空数组时循环不执行返回 0符合「没有块」的语义。复杂度时间 O(n)空间 O(1)。2.1 为什么贪心是最优的设第一个合法切点在i。任何方案的第一块都必须至少包含前i1个元素否则这块的最大值会大于它的结束下标排序后无法归位。因此第一块不可能更短切在i不会比任何其他方案差。切完之后问题在剩余部分上独立归纳可得贪心取到最大块数。这个论证依赖「排列」这一前提。若数组有重复值前缀最大值等于下标不再等价于「前缀恰好是某个值域集合」贪心失效。三、含重复值的版本LeetCode 768 允许数组中有重复元素要求同样切块后各自排序、拼接后整体非递减。此时 769 的贪心不再适用。一个通用解法是单调栈// 适用于 C17#include algorithm#include stack#include vectorint maxChunksToSortedII(const std::vectorint arr) {std::stackint st;for (int x : arr) {if (st.empty() || x st.top()) {st.push(x);} else {int top st.top();while (!st.empty() st.top() x) {st.pop();}st.push(top);}}return static_castint(st.size());}思路是栈中每个元素代表一个「当前块的 Maximum」。遇到比栈顶大的值它可以独立成块直接入栈遇到比栈顶小的值说明它必须和前面的若干块合并于是把那些最大值大于它的块全部弹出最后把原来的最大块值压回去表示合并后的块。对 769 的输入这个算法同样给出正确答案只是多用了 O(n) 的栈空间。面试时如果先给出 769 的 O(1) 贪心再补充这个通用版本能体现对问题约束的理解。版本约束时间空间方法7690..n-1的排列O(n)O(1)前缀最大值768允许重复O(n)O(n)单调栈通用任意整数O(n log n)O(n)排序后比较前缀完整示例// 适用于 C17#include algorithm#include iostream#include vectorint maxChunksToSorted(const std::vectorint arr) {int chunks 0;int curMax -1;for (int i 0; i static_castint(arr.size()); i) {curMax std::max(curMax, arr[i]);if (curMax i) {chunks;}}return chunks;}int main() {const std::vectorstd::vectorint cases{{4, 3, 2, 1, 0},{1, 0, 2, 3},{0, 1, 2, 3},{2, 0, 1}};for (const auto arr : cases) {std::cout maxChunksToSorted(arr) \n;}return 0;}按标准语义四个用例的结果分别是 1、3、4、1。逐个代入验证{4,3,2,1,0}i0到i3时前缀最大值都是 4不等于下标只有i4时curMax 4块数加一最终为 1。只能整体成块。{1,0,2,3}i0时curMax1不等于 0i1时curMax1等于 1记一块i2时curMax2再记一块i3时curMax3再记一块共 3 块。{0,1,2,3}每个位置都满足curMax i共 4 块。{2,0,1}i0时curMax2i1时仍是 2i2时curMax2等于下标记一块最终为 1。这段推演说明贪心条件看起来简单仍然要拿具体数组逐项代入。尤其是最后一个用例凭直觉容易把「3 个元素」直接当成「3 块」而实际上前两个位置的最大值都超过了各自的下标必须合并成一个整体。常见坑点❌ 对每个前缀排序后与0..i比较cpp for (int i 0; i n; i) { std::vectorint tmp(arr.begin(), arr.begin() i 1); std::sort(tmp.begin(), tmp.end()); // 再逐个比较 } ✅ 用前缀最大值O(n) 完成。❌ 把条件写成curMax icpp if (curMax i) chunks; // 排列下恒成立块数等于 n✅ 必须是curMax i。❌curMax初始化为 0遇到全负或非排列输入出错。✅ 用-1或arr[0]作为初值并在循环内统一更新。❌ 把 769 的贪心用于含重复值的数组cpp // arr {1, 1, 0}前缀最大值条件失效 ✅ 改用单调栈或排序后比较前缀。❌ 循环里int i与arr.size()直接比较触发符号警告。✅ 先把长度转成int或用std::size_t并配套转换。❌ 忘记空数组cpp int curMax arr[0]; // 空数组越界✅ 用-1 初始化并让循环自然处理空输入。❌ 认为「块内元素必须连续」。✅ 块内元素不要求连续只要求排序后能拼成整体有序{1,0}是一块元素并不连续。总结要点结论核心条件前缀最大值等于当前下标时间复杂度O(n)空间复杂度O(1)前提数组是0..n-1的排列重复值版本单调栈O(n) 空间这道题的价值在于展示「约束如何简化算法」一旦知道数组是排列判断一块是否合法只需要一个前缀最大值。离开这个约束同样的贪心立刻失效必须换用单调栈。读题时先确认约束再选方法比记住某个模板更重要。
企业数字化 ERP 产品动态
相关推荐
第 7 集:用了 CompletableFuture,两个查询为什么还是串行? 资深 Java 面试到 Agent 开发工程师 第 7 集
假设要生成一份报表,需要分别查询订单金额和退款金额,两个查询互不依赖。很容易写出这样的代码:
CompletableFuture<Integer> orders CompletableFuture.supplyAsync(() -> queryOrde… · 2026/11/2 23:40:36
多智能体一: Sequential Pipeline 流水线式Agent协同 导读:随着大模型Agent从单智能体走向多智能体集群,单一Agent全包全干的模式暴露出能力混杂、精度不足、不可迭代、难以复盘等大量工程问题。在众多多智能体编排模式中,Sequential Pipeline(顺序流水线)是落地最简单、稳… · 2026/11/2 23:40:36
OpenClaw与LINE集成提升养虾业沟通效率 1. OpenClaw与LINE集成的核心价值OpenClaw作为一款多平台即时通讯聚合工具,其与LINE的深度整合为养虾行业带来了前所未有的沟通效率提升。传统养虾场管理中,技术人员、养殖工人、销售团队之间的信息传递往往存在滞后性,而通过OpenClaw接入LIN… · 2026/11/2 23:59:44
Java record 规范构造器能否被反序列化或反射绕过 问题现象
record 类型通常靠规范构造器来把守不变式,比如禁止名字为空:
public record User(String name) {public User {if (name == null || name · 2026/11/2 23:59:44
Unity Package Manager刷新错误排查与解决方案 1. Unity Package Manager刷新资源报错问题解析最近在Unity开发过程中,Package Manager提示"Error Refreshing Asset"的问题困扰了不少开发者。作为Unity项目依赖管理的核心组件,Package Manager一旦出现问题,轻则影响资源加载&… · 2026/11/2 23:59:44
淘宝商品详情API开发实战与性能优化指南 1. 淘宝商品详情API核心功能解析淘宝开放平台的taobao.item.get接口是电商开发者最常调用的核心接口之一,它提供了获取单个商品完整信息的能力。这个接口的独特之处在于它能返回商品的基础信息、SKU数据、促销信息等全维度数据,且响应速度控制在200ms以内… · 2026/11/2 23:59:44
JJencode编码解密工具:前端JS代码混淆与源码还原实操 做前端的人大概都遇到过这种场面:交付给客户的是一个纯前端跑的小工具,或者一个 H5 小游戏,代码打包压缩完往服务器一放,懂行的人按 F12 就能把逻辑看个七七八八,改个变量名、换个皮肤就成他自己的了。这时候就会有人翻… · 2026/11/2 23:57:44
Apache HttpAsyncClient选型指南:Default与Minimal对比 1. 项目概述在构建高性能HTTP客户端时,Apache HttpAsyncClient提供了两个核心实现:DefaultHttpAsyncClient和MinimalHttpAsyncClient。这两个客户端的选择往往让开发者陷入纠结,特别是在生产环境部署时。作为在分布式系统领域深耕多年的架构师… · 2026/11/2 23:55:43
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 4:42:45
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 5:17:18
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 4:42:45
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 5:17:18