334. 递增的三元子序列

334. 递增的三元子序列,第1张

334. 递增的三元子序列

334. 递增的三元子序列

难度中等464

给你一个整数数组 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 <= 5 * 105-231 <= nums[i] <= 231 - 1

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

思路一:实际上我们很容易就可以想到这是一个求最长上生子序列长度>=3的问题,然后直接套模板就ok~

class Solution {
public:
    bool increasingTriplet(vector& nums) {
        int dp[5], cnt = 1, n = nums.size(), *p;
        dp[0] = nums[0];
        for(int i = 1; i < n; ++ i){
            p = lower_bound(dp, dp + cnt, nums[i]);
            if(p == dp + cnt){
                ++ cnt;
            }
            *p = nums[i];
            if(cnt >= 3)return true;
        }
        return false;
    }
};

思路二:实际上我们即使用优化的最长上升子序列DP也只能做到O(nlgn),况且本题要求的长度大于3即可,是不是有点大材小用了。回过头来想,只要我们能找到一个长度为3的上升序列,那么本题就已经解了。假设我们现在考虑nums[j]是否能成为一个长度为3的子序列的中值,那么我们只需要找到一个ij,有nums[j]

再想一下,其实我们关系的是大小关系而不是数值,举例来说3 4 5,3和4都小于5,我们只关心5的左边是否有数小于5,而并不关心具体的数值是多少。同时,为了能够使得nums[i]可以成为中值,我们只需要考虑1~i-1上的最小值即可,右端同理。

因此我们定义两个数组left_min与right_max,维护左边的最小值和右边的最大值,那么只要找到一个i,有left_min[i]

class Solution {
public:
    bool increasingTriplet(vector& nums) {
        int i, n = nums.size(), left_min[n + 5], right_max[n + 5];
        left_min[0] = 0x7fffffff;
        right_max[n - 1] = 0;
        for(i = 1; i < n; ++ i){
            left_min[i] = min(left_min[i - 1], nums[i - 1]);
        }
        for(i = n - 2; i >= 0; -- i){
            right_max[i] = max(nums[i + 1], right_max[i + 1]);
            if(left_min[i] < nums[i] && nums[i] < right_max[i]) return true;
        }
        return false;
    }
};

思路三:此时我们不再去看一个数nums[i]是否能成为中值,而是考虑保存第一第二的值,来考虑第三个值。基于贪心策略,我们第一第二的值越小,可以构成长度为3的上升序列概率越大。

记第一个数为first,第二个数为second,算法流程如下

1.first=nums[0],second=0x7fffffff

2.遍历1~n-1位置的元素,按照如下方式更新

        A.nums[i] > second,此时first、second、nums[i]就构成了我们需要的序列

        B.nums[i] <= second && nums[i] > first,此时很显然我们可以将second变小(或不变),second变小,后面的数就更可能大于second,即构成长度为3的上升序列

        C.nums[i] < first,这时候我们只要让first = nums[i],w h y ?

                其实是这样的,我们可以考虑一个例子如下:

                        3 4 1 6

                当我们遍历到1的时候,直觉告诉我们可以调小first,但我们是不可以把second=旧的first的,因为旧的first一定在当前位置i前面,这样赋值是不满足子序列的顺序要求的,这是第一点。第二点是,如果我们此时将second=0x7fffffff,那么我们会错过满足条件的序列3 4 6。也就是说我们用旧的second和新的first,就可以同时考虑两个序列。而这种机制也不会有问题,因为这种机制只是保证在second发生实质性的更新前可以考虑旧的first-second序列,对上例来说就是在遍历6的时候由于seance=4,其实还考虑的旧的序列3 4。

                而当second发生更新,则说明在second未更新这段时间内,并没有能够与旧序列拼成长度为3的上升子序列的情况,例子如下:

                        3 4 0 0 0 1

                如上例所示,在第二个0到第三个0时,second并未更新,然而这段区间上也找不到第三个位置的数,遍历到1的时候,second可以更新,此时对于1后面的数来说,考虑这个更小的second会更容易组成长度为3的上升序列。

class Solution {
public:
    bool increasingTriplet(vector& nums) {
        int i, n = nums.size(), first = nums[0], second = 0x7fffffff;
        for(i = 1; i < n; ++ i){
            if(nums[i] > second) return true;
            else if(nums[i] > first){
                second = nums[i];
            }else{
                first = nums[i];
            }
        }
        return false;
    }
};

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/zaji/5703106.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存