334. 递增的三元子序列
问题描述
给定一个整数数组 nums,判断这个数组中是否存在长度为 3 的递增子序列(即存在下标 i < j < k 使得 nums[i] < nums[j] < nums[k])。要求算法时间复杂度为 O(n),空间复杂度为 O(1)。
示例:
输入:nums = [1,2,3,4,5]
输出:true // 任何三个连续元素都满足
输入:nums = [5,4,3,2,1]
输出:false // 没有递增的三元组
输入:nums = [2,1,5,0,4,6]
输出:true // [1,5,6] 或 [0,4,6]
算法思路
贪心策略(双指针):
- 维护两个变量:
first:当前找到的最小候选值(作为三元组的第一个元素)second:当前找到的次小候选值(作为三元组的第二个元素,且满足first < second)
- 遍历数组:
- 若当前数
num > second→ 找到三元组,返回true - 若
num <= first→ 更新first = num(尝试更小的起点) - 若
first < num <= second→ 更新second = num(优化中间值)
- 若当前数
- 核心思想:
- 通过不断降低
first和second的值,为后续元素创造更大的匹配空间 - 即使更新了
first,之前合法的second仍然有效(隐含保留了历史信息)
- 通过不断降低
代码实现
class Solution {
public boolean increasingTriplet(int[] nums) {
// 初始化两个候选值为最大值
int first = Integer.MAX_VALUE;
int second = Integer.MAX_VALUE;
for (int num : nums) {
if (num > second) {
// 找到大于 second 的数,形成三元组
return true;
} else if (num > first) {
// 更新次小候选值(保证 first < second)
second = num;
} else {
// 更新最小候选值(可能破坏 first < second 关系,但后续会修复)
first = num;
}
}
return false;
}
}
算法分析
- 时间复杂度:O(n)
仅需遍历数组一次,每个元素处理时间为 O(1) - 空间复杂度:O(1)
仅使用两个额外变量
算法过程
nums = [2,1,5,0,4,6] :
- 初始:
first = ∞,second = ∞ num=2→ 更新first=2(路径:2)num=1→ 更新first=1(路径:1)num=5→ 更新second=5(路径:1→5)num=0→ 更新first=0(路径:0)num=4→ 更新second=4(路径:0→4)num=6→6>4→ 找到三元组0→4→6,返回true
关键点
-
贪心策略有效性:
- 更新
first不会破坏已存在的second(second隐含了比它小的历史值) - 示例:当
first=1更新为first=0后,second=5仍然有效(隐含了1→5的路径)
- 更新
-
等号处理:
- 严格递增要求:遇到相等值时跳过更新(如
num <= first包含等号) - 示例:
[1,1,1]不会触发任何更新
- 严格递增要求:遇到相等值时跳过更新(如
-
候选值关系:
- 总保持
first <= second(可能暂时不满足first < second,但遇到中间值会修复) - 示例:当
first=0时,遇到4会修复为first=0 < second=4
- 总保持
边界处理
| 边界情况 | 处理方式 |
|---|---|
| 数组长度小于 3 | 直接返回 false |
| 全递减序列 | 持续更新 first,不会触发 true |
| 重复元素 | 等号确保跳过重复值 |
| 三元组出现在序列末尾 | 遍历到最后才触发 num > second 的条件 |
测试用例
public static void main(String[] args) {
Solution solution = new Solution();
// 标准递增
System.out.println(solution.increasingTriplet(new int[]{1,2,3})); // true
// 三元组不连续
System.out.println(solution.increasingTriplet(new int[]{2,1,5,0,4,6})); // true
// 全递减
System.out.println(solution.increasingTriplet(new int[]{5,4,3,2,1})); // false
// 有重复值
System.out.println(solution.increasingTriplet(new int[]{1,1,1,1,1})); // false
// 最小值在末尾
System.out.println(solution.increasingTriplet(new int[]{2,3,1,4})); // true (2->3->4)
// 边界长度
System.out.println(solution.increasingTriplet(new int[]{1,2})); // false
}
常见问题
-
为什么更新
first后不会丢失历史信息?
虽然first被更新为更小的值,但second仍然保存着之前有效的中间值。当后续遇到比second大的数时,隐含的历史小值(可能是更新前的first)仍然与second构成二元组。 -
遇到重复元素如何处理?
条件num <= first和num <= second中的等号确保重复元素不会破坏候选关系,且不会形成非严格递增序列。 -
算法是否总能找到最长的递增序列?
不是,该算法仅检测是否存在三元组,不保证找到最长序列(如[1,3,2,4]可能检测1→3→4或1→2→4)。
3608

被折叠的 条评论
为什么被折叠?



