1. 题目
读题
考查点
这道题的考查点是:
- 如何定义合适的状态,即dp[i]的含义。
- 如何找到状态转移方程,即dp[i]和dp[j]的关系。
- 如何初始化和更新状态,即dp[i]的初始值和最大值。
- 如何从状态数组中得到最终结果,即res的值。
2. 解法
思路
讲一下思路吧。
这个问题的核心是找到数组中的最长递增子序列,也就是说,找到一个子序列,使得它的元素从左到右递增,并且长度最大。
我们可以用动态规划的方法来解决这个问题,动态规划的思想是把一个大问题分解成若干个小问题,并且利用已经解决的小问题的结果来解决大问题。
具体来说,我们可以定义一个状态dp[i],表示以nums[i]为结尾的最长递增子序列的长度。也就是说,如果我们知道了dp[i]的值,那么我们就知道了以nums[i]结尾的最长递增子序列是什么。
那么,如何求解dp[i]呢?我们可以从前往后遍历数组,对于每个元素nums[i],我们可以遍历它之前的所有元素nums[j],如果nums[j] < nums[i],那么说明nums[j]和nums[i]可以构成一个递增子序列,而且这个递增子序列的长度就是dp[j] + 1。所以,我们可以用dp[j] + 1来更新dp[i],取最大值。这样,我们就得到了以nums[i]为结尾的最长递增子序列的长度。
最后,我们只需要遍历一遍dp数组,找到其中的最大值,就是整个数组的最长递增子序列的长度了。
这道题的状态转移公式是:
dp[i]=0≤j<i and nums[j]<nums[i]max(dp[j]+1)
也就是说,dp[i]等于在所有满足nums[j] < nums[i]的j中,找到最大的dp[j] + 1。
这个公式的意义是,对于每个元素nums[i],我们要找到它之前的所有比它小的元素nums[j],并且找到这些元素中最长递增子序列的长度dp[j],然后加上1,就是以nums[i]为结尾的最长递增子序列的长度了。
代码逻辑
代码的逻辑如下:
- 首先,创建一个dp数组,dp[i]表示以nums[i]为结尾的最长递增子序列的长度。
- 然后,初始化dp数组,每个元素最小都为1,因为每个元素自己就是一个长度为1的递增子序列。
- 接着,创建一个变量res,用来记录最终的结果,初始值为0。
- 然后,遍历数组,从第二个元素开始,对于每个元素nums[i],我们可以遍历它之前的所有元素nums[j],如果nums[j] < nums[i],那么说明nums[j]和nums[i]可以构成一个递增子序列,而且这个递增子序列的长度就是dp[j] + 1。所以,我们可以用dp[j] + 1来更新dp[i],取最大值。这样,我们就得到了以nums[i]为结尾的最长递增子序列的长度。
- 最后,遍历一遍dp数组,找到其中的最大值,赋给res,就是整个数组的最长递增子序列的长度了。
具体实现
class Solution {
public int lengthOfLIS(int[] nums) {
// 创建一个dp数组,dp[i]表示以nums[i]为结尾的最长递增子序列的长度
int[] dp = new int[nums.length];
// 初始化dp数组,每个元素最小都为1
for (int i = 0; i < nums.length; i++) {
dp[i] = 1;
}
// 创建一个变量res,用来记录最终的结果
int res = 0;
// 遍历数组,从第二个元素开始
for (int i = 1; i < nums.length; i++) {
// 遍历当前元素之前的所有元素
for (int j = 0; j < i; j++) {
// 如果当前元素大于之前的元素,说明可以构成递增子序列
if (nums[i] > nums[j]) {
// 更新dp[i],取最大值
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
// 更新res,取最大值
res = Math.max(res, dp[i]);
}
// 返回结果
return res;
}
}