300. 最长上升子序列
思路:题目:给定一个无序的整数数组,找到其中最长上升子序列的长度。
例如:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
看到题目首先考虑动态规划,令dp[i]为以nums[i]结尾的最长上升子序列长度,所以有动态方程:
if(nums[j]<nums[i])
dp[i] = Math.max(dp[i],dp[j]+1);
其中j是比i小的数,遍历i和j即可求得dp[nums.length-1],于是我开始写的代码如下:
public int lengthOfLIS(int[] nums) {
if(nums.length == 0) return 0;
int[] dp = new int[nums.length];
dp[0] = 1;
for(int i=1;i<nums.length;i++){
for(int j=0;j<i;j++){
if(nums[j]<nums[i])
dp[i] = Math.max(dp[i],dp[j]+1);
}
}
return dp[nums.length-1];
}
果然一运行就出现了错误,原因是最长上升子序列不一定是要以nums的最后一个元素结尾的,所以直接返回dp[nums.length-1]是错误的。因此我们要求的应该是dp[i]中的最大的一个,于是我用res来保存这个最大值:
public int lengthOfLIS(int[] nums) {
if(nums.length == 0) return 0;
int[] dp = new int[nums.length];
dp[0] = 1;
int res = 0;
for(int i=1;i<nums.length;i++){
for(int j=0;j<i;j++){
if(nums[j]<nums[i])
dp[i] = Math.max(dp[i],dp[j]+1);
}
res = Math.max(res, dp[i]);
}
return res;
}
然后跑一下又出现了问题,原因就是我只初始化dp数组的第0个元素,而每个元素都可以作为最长上升子序列的第一个元素(刚才的方法我相当于只考虑了第一个元素作为开头,其他元素完全没有考虑其初始化),所以所有dp数组的元素最开始都需要被初始化为0,修改后的代码如下:
public int lengthOfLIS(int[] nums) {
if(nums.length == 0) return 0;
if(nums.length == 1) return 1;
int[] dp = new int[nums.length];
int res = 0;
Arrays.fill(dp, 1);
for(int i = 1; i < nums.length; i++) {
for(int j = 0; j < i; j++) {
if(nums[j] < nums[i]) dp[i] = Math.max(dp[i], dp[j] + 1);
}
res = Math.max(res, dp[i]);
}
return res;
}
其中加上if(nums.length == 1) return 1;是因为我的第一个for循环是从1开始的,没有考虑数组只有一个元素的情况,当然,如果第一个for循环是从0开始的,可以去掉这个判断。
另外这里初始化最好不要初始化res为Integer.MIN_VALUE这样如果数组长为1时会出错,因为会直接输出Integer.MIN_VALUE,当然如果你加上了这一句if(nums.length <= 1) return nums.length;那就当我没说😂。