Leetcode 334-递增的三元子序列

该博客介绍了如何在给定的整数数组中判断是否存在长度为3的递增子序列。提出了三种方法,包括动态规划、双向遍历和贪心算法,其中贪心算法实现了O(n)的时间复杂度和O(1)的空间复杂度。通过维护两个变量first和second,始终尝试找到更小的元素来更新这两个变量,以确保找到递增的三元子序列。当找到一个元素大于second时,说明存在递增子序列并返回true。否则,在遍历结束后返回false。
摘要由CSDN通过智能技术生成

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

如果存在这样的三元组下标 (i, j, k) 且满足 i < j < k ,使得 nums[i] < nums[j] < nums[k] ,返回 true ;否则,返回 false 。
在这里插入图片描述
进阶:你能实现时间复杂度为 O(n) ,空间复杂度为 O(1) 的解决方案吗?

题解

法一 动态规划 O(n^2)+O(n)

法二 双向遍历 (O(n)+O(n))

题解转载自Leetcode官方
如果数组nums 中存在一个下标 i 满足 1≤i<n−1,使得在 nums[i] 的左边存在一个元素小于
nums[i] 且在 nums[i] 的右边存在一个元素大于 nums[i],则数组 nums 中存在递增的三元子序列。
创建两个长度为 n 的数组 leftMin 和 rightMax,对于 0≤i<n,leftMin[i] 表示 nums[0] 到 nums[i] 中的最小值,rightMax[i] 表示 nums[i] 到 nums[n−1] 中的最大值。

法三 贪心法(O(n)+O(1))

可以使用贪心的方法将空间复杂度降到 O(1)。从左到右遍历数组 nums,遍历过程中维护两个变量 first 和 second,分别表示递增的三元子序列中的第一个数和第二个数,任何时候都有 first<second。

初始时,second=+∞。 对于 1≤i<n,当遍历到下标 i 时,令 num=nums[i],进行如下操作:

  1. 如果 num>second,则找到了一个递增的三元子序列,返回 true;
  2. 否则,如果 num>first,则将 second 的值更新为 num;
  3. 否则,将 first 的值更新为 num。
  4. 如果遍历结束时没有找到递增的三元子序列,返回 false。
上述做法的贪心思想是:为了找到递增的三元子序列,first 和 second 应该尽可能地小,此时找到递增的三元子序列的可能性更大。

假设 (first,second,num) 是一个递增的三元子序列,如果存在 second’ 满足first<second’<second 且 second’ 的下标位于 first 的下标和 num 的下标之间,则 (first,second’,num) 也是一个递增的三元子序列。但是当 (first,second’,num) 是递增的三元子序列时,由于 num 不一定大于 second,因此 (first,second,num) 未必是递增的三元子序列。由此可见,为了使找到递增的三元子序列的可能性更大,三元子序列的第二个数应该尽可能地小,将 second’ 作为三元子序列的第二个数优于将 second 作为三元子序列的第二个数。

同理可得,三元子序列的第一个数也应该尽可能地小。

如果遍历过程中遇到的所有元素都大于 first,则当遇到 num>second 时,first 一定出现在 second 的前面,second 一定出现在num 的前面,(first,second,num) 即为递增的三元子序列。

上面的求解过程中有个问题:当已经找到了长度为 2 的递增序列,这时又来了一个比 small 还小的数字,为什么可以直接替换first呢,这样 first和 mid 在原数组中并不是按照索引递增的关系呀?

Trick 就在这里了!假如当前的 first和second为 [3, 5],这时又来了个 1。假如我们不将first替换为 1,那么,当下一个数字是 2,后面再接上一个 3 的时候,我们就没有办法发现这个 [1,2,3] 的递增数组了!也就是说,我们替换最小值,是为了后续能够更好地更新中间值!

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

根据上述分析可知,当遇到 num>second 时,一定存在一个递增的三元子序列,该三元子序列的第二个数和第三个数分别是 second 和 num,因此返回 true。

class Solution {
    public boolean increasingTriplet(int[] nums) {
        int len = nums.length;
        int first = nums[0],second=Integer.MAX_VALUE;
        //从1开始!
        for(int i=1;i<len;i++){
            //构成递增三原子序列
            if(nums[i]>second) return true;
            //为什么first=i?这样不会使得first在second后面吗?如果不替换,后面出现nums[i],不就可以直接接在second后面了吗?
            //不会.有一个比first大比second小的前·最小值出现在second之前。因此,当后续出现比second大的值的时候,我们一样可以通过当前first和second推断的确存在着长度为 3 的递增序列
            else if(nums[i]<first) first=nums[i];
            //写清楚,不要直接else,这样可以过滤掉nums[i]>=first的情况
            else if(nums[i]<second&&nums[i]>first) second=nums[i];
        }
        return false;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值