LeetCode算法日记 - Day 89: 最长递增子序列
目录
1. 最长递增子序列
1.1 题目解析
1.2 解法
1.3 代码实现
1. 最长递增子序列
https://leetcode.cn/problems/longest-increasing-subsequence/
给你一个整数数组 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
提示:
1 <= nums.length <= 2500-104 <= nums[i] <= 104
进阶:
- 你能将算法的时间复杂度降低到
O(n log(n))吗?
1.1 题目解析
题目本质
这道题要解决的核心问题是:从数组中挑选出一些数字(保持原顺序),使得它们严格递增,求最多能挑几个。注意这里是"子序列"不是"子数组",意味着不需要连续,只要相对位置不变就行。
常规解法
最直观的想法是:暴力枚举所有可能的子序列,检查每个是否递增,记录最长的。比如对于 [10,9,2,5],要检查 [10]、[10,9]、[10,2]、[9,2]、[2,5]、[10,9,2]... 所有组合。
问题分析
暴力法的问题在于组合数太多。长度为 n 的数组有 2n2n 个子序列,每次还要检查是否递增,时间复杂度 O(2^n⋅n),当 n=2500 时根本跑不动。
思路转折
要想高效 → 必须避免重复计算 → 用动态规划。
关键洞察:如果我知道前面每个位置能组成的最长递增序列,当前位置就可以直接利用这些结果。具体来说,站在位置 i 时:
-
回头看所有位置 j < i
-
如果 nums[i] > nums[j],说明可以把 nums[i] 接在 j 位置的序列后面
-
接上后长度变成 dp[j] + 1
-
遍历所有可能的 j,选长度最大的
这样每个位置只算一次,复杂度降到 O(n^2) 可以通过。
1.2 解法
算法思想
定义 dp[i] = 以 nums[i] 结尾的最长递增子序列长度
递推公式:
dp[i] = max(dp[j] + 1) 其中 j < i 且 nums[i] > nums[j]初始值:dp[i] = 1 (每个元素自己就是长度1)
i)初始化 dp 数组,所有值设为 1(每个元素单独都是长度1的序列)
ii)外层循环:从左到右遍历每个位置 i(从1到n-1)
iii)内层循环:对于当前位置 i,遍历它前面的所有位置 j(从0到i-1)
-
如果 nums[i] > nums[j],说明可以接在 j 后面
-
更新 dp[i] = Math.max(dp[i], dp[j] + 1)
iv)遍历整个 dp 数组,找出最大值作为答案
易错点
-
误以为只看相邻元素:新手容易写成 if(nums[i] > nums[i-1]),但递增子序列不要求连续,必须遍历所有 j < i
-
忘记初始化为1:如果初始化为0,即使找不到可接的位置,dp[i] 也应该是1(元素自己),所以必须 Arrays.fill(dp, 1)
-
直接返回 dp[n-1]:dp[i] 只表示"以i结尾"的长度,最长序列不一定在最后,必须取 max(dp)
-
边界条件:外层循环从 i=1 开始(i=0 时没有前面的元素可比较),但要确保 n=1 时能正确返回1
1.3 代码实现
class Solution {public int lengthOfLIS(int[] nums) {int n = nums.length;int[] dp = new int[n];Arrays.fill(dp, 1); // 初始化:每个元素单独都是长度1// 从第二个元素开始遍历for(int i = 1; i < n; i++){// 回头看所有前面的位置for(int j = 0; j < i; j++){// 如果能接在j后面,更新最大长度if(nums[i] > nums[j]){dp[i] = Math.max(dp[i], dp[j] + 1);}}}// 找出所有位置中的最大值int ret = 0;for(int x : dp){ret = Math.max(ret, x);}return ret;}
}
复杂度分析
-
时间复杂度:O(n^2),两层循环,外层 n 次,内层最多 n 次
-
空间复杂度:O(n),需要 dp 数组存储每个位置的结果
