题目
给你一个整数数组 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 。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/longest-increasing-subsequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
一、O(n^2^)的解
动态规划,从左到右模型
dp[i]子序列必须以i位置的数结尾的情况下,最长递增子序列是多长?整个中求max就是答案
每次求dp[i]时,遍历数组0到i-1,找出比nums[i]小的数的位置j,
dp[i]=Math.max(dp[i],dp[j]+1);
代码
class Solution {
public int lengthOfLIS(int[] nums) {
int n=nums.length;
if(n==0){
return 0;
}
int max=1;
int[] dp=new int[n];
dp[0]=1;
for(int i=1;i<n;i++){
dp[i]=1;
for(int j=0;j<i;j++){
if(nums[i]>nums[j]){
dp[i]=Math.max(dp[i],dp[j]+1);
}
}
max=Math.max(max,dp[i]);
}
return max;
}
}
最优解
辅助end数组
end[i]:目前所有长度为i+ 1的递增子序列中的最小结尾
如何填写end?
每次遍历到一个数num[i],用二分在end数组中找到最左大于num[i]的位置j
end[j]=num[i];
如果没有大于num[i]的数,则end[j+1]=num[i]
你把一个值塞进去之后,改写的位置含义是对的,没改写的位置继续保证正确的含义
public static int lengthOfLIS(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
int[] ends = new int[arr.length];
ends[0] = arr[0];
int right = 0;
int l = 0;
int r = 0;
int m = 0;
int max = 1;
for (int i = 1; i < arr.length; i++) {
l = 0;
r = right;
while (l <= r) {
m = (l + r) / 2;
if (arr[i] > ends[m]) {
l = m + 1;
} else {
r = m - 1;
}
}
right = Math.max(right, l);
ends[l] = arr[i];
max = Math.max(max, l + 1);
}
return max;
}