Leetcode 300. 最长递增子序列

300. 最长递增子序列

//我居然为了一个四月勋章提交了题解区的答案,这不是勋章,是耻辱。

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

示例 2:

输入:nums = [0,1,0,3,2,3]
输出:4

示例 3:

输入:nums = [7,7,7,7,7,7,7]
输出:1

提示:

  • 1 <= nums.length <= 2500
  • -104 <= nums[i] <= 104

进阶:

  • 你可以设计时间复杂度为 O(n2) 的解决方案吗?
  • 你能将算法的时间复杂度降低到 O(n log(n)) 吗?
/**
    动态规划
    dp[j] j位前比nums[j]小的个数
    dp[j]={
        max(dp[i]),i=0..j&&nums[i]<nums[j]
    }
**/

class Solution {
    public int lengthOfLIS(int[] nums) {
        int n=nums.length;
        if(n==0)return 0;
        int max=Integer.MIN_VALUE;
        int[] dp=new int[n];
        dp[0]=1;
        for(int i=1;i<n;i++){
            for(int j=0;j<i;j++){
                if(nums[j]<nums[i]){
                    dp[i]=Math.max(dp[i],dp[j]);//与左边符合条件的长度比较取最长
                }
            }
            dp[i]+=1;//加上本轮j
            max=Math.max(max,dp[i]);
        }
        return n==1?dp[0]:max;
    }
}

 

/**写得最认真的博客没有之一
进阶:算法时间复杂度降到O(nlogn)--想到二分,那么需要实现一个有序数组查找
    贪心:使序列最右侧的最大值尽量小可以得到最长子序列
    dp[len]表示长度为len时最右一位元素值,需证明此数组单调递增
    即证i<j时,dp[i]<dp[j]
    反证i<j时,dp[i]>dp[j]:
        令长度为j的序列减去后j-i个元素,得到最小元素min<dp[j],因为dp[j]是序列最大元素
        而min<dp[j]<dp[i],说明dp[i]不是长度为i的序列的最右侧的最小元素,反证不成立。

    算法实现思路:
        当前遍历到nums[i]
        如果nums[i]>dp[i],dp[++i]=nums[i]
        如果nums[i]<dp[i],在已有的dp数组中二分查找比nums[i]小的dp数,替换下一个长度的dp为nums[i]
        (这个处理真的好妙啊,最小数的修改维护了已有长度,又给后续长度增长提供了新的最小值
        最后一个选择成功的dp[]下标就是最长长度啦
**/
class Solution {
    public int lengthOfLIS(int[] nums) {
        int n=nums.length;
        if(n==0)return 0;
        int[] dp=new int[n+1];
        int len=1;
        dp[len]=nums[0];//长度为1的子序列初始化为数组第一个值
        for(int i=1;i<nums.length;i++){
            if(nums[i]>dp[len]){
                dp[++len]=nums[i];
            }else{
                int l=1,r=len,pos=0;
                while(l<=r){
                    int mid=(l+r)/2;
                    if(dp[mid]<nums[i]){//找最大的一个比nums[i]小的
                        pos=mid;
                        l=mid+1;
                    }else{
                        r=mid-1;
                    }
                }
                dp[pos+1]=nums[i];//替换pos+1长度的最小数,如果找不到比nums[i]小的,那么把长度为1的dp置为nums[i]
            }
        }
        return len;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值