又忘记怎么做了,是上次自己没写清楚,这里附上参考题解,作者liweiwei1419:
https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/dong-tai-gui-hua-er-fen-cha-zhao-tan-xin-suan-fa-p/
所有的方法都不是一促而就的,暴力都不会,如何想到更优化的?
这道理暴力可不可以?
肯定可以,为每一个数记录一个以它开头的最长子序列。从第一个数开始遍历一遍数组,碰到大的res就加1,直到最后一个数;
但是这样找的是以第一个数开始完全递增的,确不一定是最长的,因为大的可能在前,比如数组:1,10,4,5,100,,按这个找出来是
1,10,100,其实是14,5,100,也就是説每一个位置都可选可不选,因此复杂度是级别的,不可取。
明显上述暴力做法有很多重复的查找
为了从一个较短的上升子序列得到一个较长的上升子序列,我们主要关心这个较短的上升子序列结尾的元素。由于要保证子序列的相对顺序,在程序读到一个新的数的时候,如果比已经得到的子序列的最后一个数还大,那么就可以放在这个子序列的最后,形成一个更长的子序列;
代码
import java.util.*;
class Solution {
public int lengthOfLIS(int[] nums) {
if(nums == null || nums.length == 0)
return 0;
int len = nums.length;
// dp[i]表示以nums[i]结尾的最长上升子序列
int dp[] = new int[len];
//初始化
Arrays.fill(dp, 1);
int res = 0;
//填数组
for(int i = 1; i < len; i++){
//dp[j]表示以之前数结尾的最长序列,要和nums[i]比较
for(int j = 0; j < i; ++j){
dp[i] = dp[j];
if(nums[j] < nums[i]){
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
res = Math.max(dp[i], res);
}
return res;
}
}
提交报错:
分析:初始化dp都为1,但是循环是从下标1开始的,只有一个数情况,不会走循环,所以res初始化应该为1或者for循环i从0开始
再次提交又发现错误,原来是赋值后就打断原来最长的情况了,删掉这一句:
删掉后运行结果:
效率比较低,下次来看方法三
明天更新
法一:明显动态规划
法二:贪心加二分查找
tail[i]代表长度为i+1的上升序列最小的末尾
记住,这个代码只保证末尾最小,也就是长度最长
最终数组不是最优结果,只求长度
如果找不到,也就是新数大于tail,则最后循环退出为right
res是最长的个数
这里有个trick,right等于res则总是指向下一个数
因此如果相等的数进来,right会左移 多写几遍
class Solution {
public int lengthOfLIS(int[] nums) {
if(nums == null || nums.length == 0)
return 0;
int n = nums.length;
// tail[i]代表长度为i+1的上升序列最小的末尾
// 记住,这个代码只保证末尾最小,也就是长度最长
// 最终数组不是最优结果,只求长度
int tail[] = new int[n];
//tail数组目前加入数的长度
int res = 0;
for(int num : nums){
int left = 0;
int right = res;
//二分,找出在tail中第一个大于等于num的数(这里一定有)
while(left < right){
int mid = left + ((right - left) >> 1);
if(num > tail[mid]) left = mid + 1;
else
right = mid;
}
//如果有相等的也是替换
tail[left] = num;
//如果找不到,也就是新数大于tail,则最后循环退出为right
//res是最长的个数
//这里有个trick,right等于res则总是指向下一个数
//因此如果相等的数进来,right会左移
if(res == right) res++;
}
for(int num : tail) System.out.print(num);
return res;
}
}