87.最长递增子序列

给你一个整数数组 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

代码:
1.动态规划(时间复杂度O(n2))

class Solution {
    public int lengthOfLIS(int[] nums) {
        //dp[i] 表示以 nums[i] 结尾的最长递增子序列(LIS)的长度
        int[] dp = new int[nums.length];
        //存储最终结果,即最长递增子序列的长度
        int res = 0;
        //遍历数组,计算每个位置 i 的 dp[i]
        for(int i = 0;i<nums.length;i++){
            //初始情况下,每个元素自身构成一个长度为 1 的递增子序列
            dp[i] = 1;
            //如果 nums[j] < nums[i],说明 nums[i] 可以接在 nums[j] 后面形成更长的递增子序列
            //更新 dp[i],取 dp[j] + 1 和当前 dp[i] 的较大值
            for(int j = 0;j<i;j++)if(nums[j]<nums[i])dp[i] = Math.max(dp[i],dp[j]+1);
        }
        //遍历 dp 数组,找到最大的值(即最长递增子序列的长度)
        for(int i = 0;i<nums.length;i++)res = Math.max(res,dp[i]);
        //返回结果
        return res;
    }
}

2.贪心+二分查找(时间复杂度O(nlogn))

class Solution {
    public int lengthOfLIS(int[] nums) {
        //p[i]表示长度为i的LIS的最小末尾元素
        //p[0]无意义,从p[1]开始使用
        int[] p = new int[nums.length+1];
        //当前LIS的最大长度
        int len = 0;
        for(int i = 0;i<nums.length;i++){
            //在p[1..len]中查找第一个>=nums[i]的位置t
            int t = lower_bound(1,len+1,nums[i],p);
            //更新p[t] = nums[i],表示长度为t的LIS的最小末尾可以更小
            p[t] = nums[i];
            //如果t超过了当前最大长度,说明可以扩展LIS
            if(t>len)len++;
        }
        //返回最长递增子序列的长度
        return len;
    }
    //在 p[l..r-1] 范围内查找第一个 ≥ target 的位置
    public int lower_bound(int l,int r,int target,int[] p){
        while(l<r){
            int mid = l+r>>1;
            if(p[mid]>=target)r = mid;
            else l = mid+1;
        }
        return l;
    }
}
posted @ 2025-05-09 09:56  回忆、少年  阅读(20)  评论(0)    收藏  举报