题目
给你一个整数数组 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;
}
}