【LeetCode】334. 递增的三元子序列

一、题目

给你一个整数数组 nums ,判断这个数组中是否存在长度为 3 的递增子序列。

如果存在这样的三元组下标 (i, j, k) 且满足 i < j < k ,使得 nums[i] < nums[j] < nums[k] ,返回 true ;否则,返回 false 。

示例 1

输入:nums = [1,2,3,4,5]
输出:true
解释:任何 i < j < k 的三元组都满足题意

示例 2

输入:nums = [5,4,3,2,1]
输出:false
解释:不存在满足题意的三元组

示例 3

输入:nums = [2,1,5,0,4,6]
输出:true
解释:三元组 (3, 4, 5) 满足题意,因为 nums[3] == 0 < nums[4] == 4 < nums[5] == 6

提示

  • 1 <= nums.length <= 105
  • -231 <= nums[i] <= 231 - 1

进阶:你能实现时间复杂度为 O(n) ,空间复杂度为 O(1) 的解决方案吗?

二、解决

1、动态规划

思路

每次寻找比当前数小的元素个数,待完善……

代码

class Solution {
    
    
    public boolean increasingTriplet(int[] nums) {
    
    
        int n = nums.length;
        int[] dp = new int[n];
        Arrays.fill(dp, 1);
        for (int i = 0; i < n; ++i) {
    
    
            for (int j = 0; j < i; ++j) {
    
    
                if (nums[j] < nums[i]) {
    
    
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
                if (dp[i] >= 3) return true;
            }
        }
        return false;
    }
}

时间复杂度 O ( n 2 ) O(n^2) O(n2)
时间复杂度 O ( n ) O(n) O(n)

2、前后遍历

思路

定义:
forward[i]:从前向后遍历,保存[0, i]之间最小元素值。
backward[i]:从后向前遍历,保存[i, n - 1]间最大元素值。

例如:
nums[i]: 8 3 5 1 6
forwa[i]: 8 3 3 1 1
backw[i]:8 6 6 6 6

代码

class Solution {
    
    
    public boolean increasingTriplet(int[] nums) {
    
    
        int n = nums.length;
        if (n < 3) return false;
        int[] f = new int[n]; f[0] = nums[0];
        int[] b = new int[n]; b[n-1] = nums[n-1];
        for (int i = 1; i < n; i++) {
    
    
            f[i] = Math.min(f[i - 1], nums[i]);
        }
        for (int i = n - 2; i >= 0; i--) {
    
    
            b[i] = Math.max(b[i + 1], nums[i]);
        }
        for (int i = 0; i < n; i++) {
    
    
            if (f[i] < nums[i] && nums[i] < b[i]) return true;
        }
        return false;
    }
}

时间复杂度 O ( n ) O(n) O(n)
时间复杂度 O ( n ) O(n) O(n)

3、双指针

思路

疑问:当已经找到长度为 2 的递增序列,又来 1 个比small小的数字,为什么可以直接替换smallsmallmid并不是按索引递增啊?

说明:假如当前small=3和 mid=5,这时来了个 1,如果不将 small 替换为 1,那当下一个数字是 2,后面再接一个 3 时,就没办法发现 [1,2,3] 递增数组了!也就是说,替换最小值是为了后续更好地更新中间值。

另外,即使更新了 small ,这个 small 在 mid 后面,没严格遵守递增顺序,但它隐含真相是,有一个数 numsmall < num && num < mid, 最小值出现在mid前。因此,当后续出现比 mid 大的值的时候,可以通过当前 smallmid 推断,的确存在着长度为 3 的递增序列。 所以,这样替换不会干扰后续计算!

代码

class Solution {
    
    
    public boolean increasingTriplet(int[] nums) {
    
    
        int small = Integer.MAX_VALUE, mid = Integer.MAX_VALUE;
        for (int num : nums) {
    
    
            if (num <= small) small = num;
            else if (num <= mid) mid = num;
            else return true;
        }
        return false;
    }
}

时间复杂度 O ( n ) O(n) O(n)
时间复杂度 O ( 1 ) O(1) O(1)

三、参考

1、C++ 线性时间复杂度详细解析,打败 98%
2、【334. 递增的三元子序列】三种思路(动态规划+双指针+前后遍历)
3、python3 扩展到一般情况,找是否存在长度为n的递增子序列
4、My way to approach such a problem. How to think about it? Explanation of my think flow.
5、My java solution works for not just triplet

猜你喜欢

转载自blog.csdn.net/HeavenDan/article/details/115015624