 时间 O(1) 空间的贪心双变量解法)
LeetCode 334 递增的三元子序列O(n) 时间 O(1) 空间的贪心双变量解法【免费下载链接】leetcodeLeetCode Solutions: A Record of My Problem Solving Journey.( leetcode题解记录自己的leetcode解题之路。)项目地址: https://gitcode.com/gh_mirrors/le/leetcode导读本篇技术指南以 leetcode 仓库中的 334.increasing-triplet-subsequence.md 为骨架完整讲解力扣 334「递增的三元子序列」一题如何在未排序数组中用 O(n) 时间、O(1) 空间的贪心策略判断是否存在长度恰好为 3 的严格递增子序列。读完本文你将掌握维护最小值与次小值两个变量的核心套路理解它为何正确、为何不能被滑动窗口替代并能直接运行仓库给出的 JS / Python3 代码。题目信息与前置知识题目地址https://leetcode-cn.com/problems/increasing-triplet-subsequence/难度分级在仓库的 collections/medium.md 中被收录于 Medium 难度列表同时出现在 README.md、SUMMARY.md 与 introduction.md 的题解索引中前置知识双指针见仓库专题文章 91/two-pointers.md 对指针思想的系统归纳题目描述与约束给定一个未排序的数组判断这个数组中是否存在长度为 3 的递增子序列。数学表达式如下如果存在这样的 i, j, k且满足0 ≤ i j k ≤ n-1使得arr[i] arr[j] arr[k]返回true否则返回false。说明要求算法的时间复杂度为 O(n)空间复杂度为 O(1)。示例 1输入:[1,2,3,4,5]输出:true示例 2输入:[5,4,3,2,1]输出:false核心思路为什么滑动窗口不可行这道题求解的是顺序数字是否存在三个递增的排列。原文档特别强调这里没有要求连续因此诸如滑动窗口的思路是不可取的。滑动窗口详见 thinkings/slide-window.md依赖窗口内元素在物理位置上连续而本题的三元组允许中间跳元素——例如[2, 1, 3, 4]中2 → 3 → 4是合法答案但它们并非相邻。同时题目要求 O(n) 的时间和 O(1) 的空间因此暴力枚举所有三元组O(n³)以及任何需要额外 O(n) 空间的方案都不在考虑范围内。贪心解法依次填充三个槽位我们的目标是依次找到三个数字使其顺序递增。因此做法是从左到右遍历数组维护三个变量分别记录最小值、第二小值、第三小值。只要能填满这三个变量就返回true否则返回false。仓库在 assets/drawio/334.increasing-triplet-subsequence.drawio 中保存了本解的思路示意图draw.io 源文件可用 draw.io 打开查看。关键点解析原文档点名的关键点只有一条维护两个变量分别记录最小值、第二小值。只要能填满三个变量就返回true否则返回false。这里的精妙之处在于第三个变量不需要真正存储。因为一旦出现一个数同时大于最小值和第二小值就说明三元组已经找到直接返回true即可——所以代码里实际只需维护n1当前最小值和n2当前第二小值两个变量。正确性直觉用反证法可以说明为什么优先更新最小值不会漏解当nums[i] n1时用它替换n1。虽然这可能把n2对应的位置挤掉n1可能来自n2之后的下标但n2仍然大于等于新的n1且n2本身的存在不依赖于它是否在n1之后——只要后续某个数大于n2它必然大于n1三元组依然成立当n1 nums[i] n2时用它替换n2使得第二小值变得更小为后续找到第三个数创造更宽松的条件当nums[i] n2时n1 n2 nums[i]已经构成递增三元组。也就是说n1与n2始终维护的是遍历前缀中最小的两个有潜力的递增值它们不要求下标递增关系这正是本题区别于普通子序列 DP 的贪心本质。代码实现代码支持 JS 与 Python3为仓库原文完整收录。JS Code/** * param {number[]} nums * return {boolean} */ var increasingTriplet function (nums) { if (nums.length 3) return false; let n1 Number.MAX_VALUE; let n2 Number.MAX_VALUE; for (let i 0; i nums.length; i) { if (nums[i] n1) { n1 nums[i]; } else if (nums[i] n2) { n2 nums[i]; } else { return true; } } return false; };对 JS 版本做两点实现层面的补充说明nums.length 3的提前返回是一处必要剪枝——少于 3 个元素不可能存在三元组n1、n2初始化为Number.MAX_VALUE正无穷大保证第一次比较必然走nums[i] n1分支从而正确初始化最小值。Python3 Codeclass Solution: def increasingTriplet(self, A: List[int]) - bool: a1 a2 float(inf) for a in A: if a a2: return True elif a a1: a2 a else: a1 a return FalsePython 版本用float(inf)初始化a1、a2语义与 JS 的Number.MAX_VALUE完全一致任何有限整数第一次都会被归入else分支成为新的a1。复杂度分析时间复杂度O(N)单次线性遍历即可完成判断空间复杂度O(1)仅使用两个常量级变量。变体与进阶扩展到任意长度的递增子序列本题是判断是否存在长度为 3 的严格递增子序列的特例其维护前缀最小序列的思想可以自然推广若题目改为是否存在长度为 k 的递增子序列可以将两个变量推广为长度为 k 的数组用同样的贪心逻辑逐个填充槽位时间复杂度仍为 O(k·N)若题目改为求最长递增子序列的长度LIS贪心法失效需要动态规划或二分 patience sorting仓库 selected/LIS.md 对 LIS 的 DP 建模做了系统讲解可作为对比阅读334 只问是否存在三元组所以贪心可行LIS 问最大长度所以必须完整求解类似的维护最小/次小值技巧在仓库 229.majority-element-ii.mdBoyer-Moore 投票的n1/n2双候选中也有体现二者虽问题不同但用少量常量变量在线处理数组的思维一致。小结解题框架单次遍历 维护最小值、第二小值两个槽位第三个值出现即返回true时间复杂度 O(N)、空间复杂度 O(1)严格满足题目约束注意不要求连续切勿误用滑动窗口该题在仓库中被标记为 推荐题解见 introduction.md是练习贪心 变量状态维护的经典入门题。【免费下载链接】leetcodeLeetCode Solutions: A Record of My Problem Solving Journey.( leetcode题解记录自己的leetcode解题之路。)项目地址: https://gitcode.com/gh_mirrors/le/leetcode创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考