经典动态规划-最长上升子序列

题目

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-increasing-subsequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

动态规划

动态规划思路很简单,就是dp[i]记录当前位置对应nums[i]最大的上升子序列长度,一路递推到最后面结果就出来了。dp[i]的含义是num[i]当前位置的最大上升子序列,求dp[i]所需要的遍历之前的所有点。

  • 时间复杂度:O(n2)
  • 空间复杂度:O(n)
class Solution {
    public int lengthOfLIS(int[] nums) 
    {
    	int n=nums.length;
        int[] dp=new int[n];
        Arrays.fill(dp, 1);
        for(int i=1;i<n;i++)
        {
        	int max=1;
        	for(int j=0;j<i;j++)
        		if(nums[i]>nums[j])
        			dp[i]=Math.max(dp[i],dp[j]+1);
        }
    	
    	int ans=Arrays.stream(dp).max().getAsInt();
    	return ans;

    }
}

贪心+二分+动态规划

如果求最长的上升子序列,其实也就是尽可能的让数增的慢一些。比如说同样的长度的序列[0,3,18][0,3,22],我们肯定优先选择后者。其中这个方法 中的dp[i]的含义是长度为i的子序列,最后一位元素最小是dp[i]。每次遍历的时候,如果nums[i]大于dp[len],则代表最长子序列更长的。如果nums[i]小于dp[len],则找到dp数组中,刚好大于nums[i]的那个位置。也就是dp[m]>nums[i],更新dp[m]nums[i],也就是让本来长度为i的上升子序列的最元素的最小值更新为nums[i]
其中dp数组肯定是严格递增的,也就是都不可能出现相等的值。这因为我们赋给dp数组的值的时候,都是要修改的位置大于前面那个位置才修改的。期间还不断的让dp[]的值更小。

  • 时间复杂度:O(n*log(n))
  • 空间复杂度:O(n)
class Solution {
    public int lengthOfLIS(int[] nums) 
    {
    	int n=nums.length;
        int[] dp=new int[n];//dp[i]表示长度为i的上升子序列末尾最小的数是dp[i]
        int len=0;
        dp[len]=nums[0];
        for(int i=1;i<n;i++)
        {
        	if(nums[i]>dp[len])
        		dp[++len]=nums[i];
        	else
        	{
        		int l=0,r=len;int pos=-1;//如果所有数都比nums[i]大,则修改dp[0]
        		while(l<=r)
        		{
        			int mid=(l+r)>>1;
        			if(nums[i]>dp[mid])
        			{
        				pos=mid;
        				l=mid+1;
        			}
        			else
        				r=mid-1;
        		}
        		//无论数组的元素是多少,只要是递增的,最后的pos所指的元素一定是第一个小于nums你要找的元素的
        		dp[pos+1]=nums[i];
        	}
        }
    	return len+1;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值