题目
给你一个整数数组 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
题解一(动态规划)
class Solution {
public int lengthOfLIS(int[] nums) {
if(nums.length==0||nums==null){
return 0;
}
int[] dp=new int[nums.length];
dp[0]=1;
Arrays.fill(dp,1);//注意!
int ans=dp[0];
for(int i=0;i<nums.length;i++){
for(int j=0;j<i;j++){
if(nums[j]<nums[i]){
dp[i]=Math.max(dp[j]+1,dp[i]);//遍历j,就可以得到最大的dp[j],由于j前面的dp可能大于j,所以还需要比较dp[j]+1和dp[i].
}
}
ans=Math.max(ans,dp[i]);//得到dp[i]数组中的最大值
}
return ans;
}
}
笔记:
- 状态转移方程:dp[i]表示在第i个元素之前的最长递增子序列的长度,dp[i]=max(dp[j])+1,其中0<=j<i且num[j]<num[i]
- 时间复杂度是O(N^2)
- 因为转移方程和dp[j]相关,所以如果不预先置1,会影响输出结果。
题解二(动态规划+二分查找 *)
class Solution {
public int lengthOfLIS(int[] nums) {
if(nums.length<=1){
return nums.length;
}
int[] tails=new int[nums.length];
tails[0]=nums[0];
int res=0;
for(int num:nums){
int i=0,j=res;
while(i<j){
int mid=i+(j-i)/2;
if(tails[mid]<num)i=mid+1;
else j=mid;
}
tails[i]=num;
if(i==res)res++;
}
return res;
}
}
笔记:
-
思路:
维护一个tails[]数组,tails[i]表示长度为i+1的子序列尾部的值,遍历原nums[]数组,
若第k个数比tails[i]小,大于tails[i-1],则用nums[k]替换掉tails[i],若第k个数大于tails[i],则append在tails[i]尾部,len+1.
-
在tails里搜寻时,因为tails是有序数组,所以使用二分搜索.
-
tails[k]的值代表长度为 k+1子序列的尾部元素值。