LeetCode 334. 递增的三元子序列
题目描述


题意分析
判断能否选出三个元素,使下标严格递增、数值也严格递增。元素不要求相邻,重复值不能用来增加递增长度。
题目只问是否存在,不需要返回具体元素。进阶要求 $O(n)$ 时间和 $O(1)$ 空间,因此可以只保留最容易接上后续元素的单元素和二元组。
解法:维护两个最小递增结尾
核心思路
[!blue]
长度相同的递增子序列,结尾越小,后续越容易接上更大的数。因此,扫描过的所有单元素中只需保留最小值
first;所有合法递增二元组中,只需保留最小的第二项second。二元组不存在时,用最大整数作为初值。对当前元素
num,按顺序处理三种情况:
num <= first:降低单元素结尾。此前最小元素都不小于num,所以它不能作为新二元组的第二项,已有的second保持不变。first < num <= second:此前的first可以接上当前元素,构成一个合法二元组。把second更新为num,得到不大于原结尾的候选。num > second:历史上已有一个以second结尾的递增二元组,当前元素出现在它之后且更大,三元组成立。
first和second是两类候选的最优结尾,不一定来自同一组下标。first后来变小甚至出现在second后面,也不影响second早先已经拥有一个更小的前驱;因此更新first时不能清空second。这种压缩不会漏解:如果存在
nums[i] < nums[j] < nums[k],扫描到j时,已有first <= nums[i] < nums[j],所以此时second <= nums[j],或更早就已经找到答案。之后second只会变小,扫描到k必然能找到第三个更大的元素。
解题步骤
- 将
first、second都初始化为最大整数,表示尚未建立对应候选。- 从左到右读取
num,先判断num <= first;成立时仅更新first。- 否则判断
num <= second;成立时更新second,它的更小前驱就是此前的first。- 两个条件都不成立,说明
num > second,立即返回true。- 遍历结束仍未找到第三项,返回
false;不足三个元素时也会自然得到这个结果。
代码实现
class Solution {
public boolean increasingTriplet(int[] nums) {
int first = Integer.MAX_VALUE;
// 记录历史递增二元组的最小结尾,不是当前第一项的固定搭档。
int second = Integer.MAX_VALUE;
for (int num : nums) {
if (num <= first) {
first = num;
} else if (num <= second) {
second = num;
} else {
return true;
}
}
return false;
}
}
func increasingTriplet(nums []int) bool {
maxInt := int(^uint(0) >> 1)
// 两个门槛分别对应一项和合法两项的最小结尾。
first, second := maxInt, maxInt
for _, num := range nums {
if num <= first {
first = num
} else if num <= second {
second = num
} else {
return true
}
}
return false
}
复杂度分析
- 时间复杂度:$O(n)$。从左到右扫描一次,每个元素只执行常数次比较与赋值。
- 空间复杂度:$O(1)$。只维护两个最小结尾,不保存子序列或下标。
关键点总结
[!green]
second是合法递增二元组的最小结尾,不是数组中第二小的数。- 较小结尾能接受所有原结尾可以接受的后续元素,因此丢弃较大结尾不会漏解。
second保留其历史前驱的存在性,不要求与当前first组成二元组。- 两处
<=吸收相等元素,只有严格更大才能增加递增长度。
易错点总结
[!yellow]
- 将
<=改成<,会让重复值进入下一层判断,误认为形成严格递增。- 三个分支必须互斥;写成独立的
if可能让当前元素刚更新完first,又参与更新second。- 更新
first时重置second,会丢掉此前已经成立的二元组。- 只检查相邻元素,或要求当前
first的下标早于当前second,都额外限制了题目允许的子序列。- 最大整数也可能是输入值,但作为初值仍安全:若
second等于最大整数,后续不可能出现比它更大的第三项,因此不会把未建立的二元组误判为答案。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 300. 最长递增子序列 | 中等 | 本题只判断LIS是否达到3,可把完整状态压缩为最小第一项与最小第二项。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!