求解最长递增子序列
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
相似题目:求解整型数组的最大子串和
求解最长递增子序列,说明这是一个“最优化”问题,并且该问题可以采用动态规划求解。
假设 dp[i] 表示数组 nums[0...i] 的最长递增子序列的长度,要注意:nums[i] 不一定是 dp[i] 所代表的最长递增子序列中的一个元素。
比如数组 [0,1,2,-1] nums[3]=-1 并不是 dp[3] 所代表的最长递增子序列中的元素。dp[3] 所代表的最长递增子序列是:[0,1,2],因为有3个元素,所以长度为3,即:dp[3]=3
要想求解 dp[i],显然 dp[0]=1
i=1,求解:dp[1],如果 nums[1] 大于 nums[0],则 dp[1]=dp[0]+1
i=2, 求解:dp[2],遍历 nums[0]、nums[1],即 j = {0,1},如果 nums[2] 比 nums[j] 要大,nums[2] 可以作为某个序列的最后一个元素,但是最大的序列长度需要遍历j = {0,1} 取最大值,即: dp[2] = max{dp[j]+1, dp[i]}
i=3, 求解:dp[3],遍历 nums[0]、nums[1]、nums[2],即 j={0,1,2},如果 如果 nums[3] 比 nums[j] 要大,nums[3] 可以作为某个序列的最后一个元素,但是最大的序列长度需要遍历j = {0,1,2} 取最大值,即: dp[3] = max{dp[j]+1, dp[i]}
...
如果最长递增子序列以 nums[i] 结尾,那么 nums[0,1...i-1] 中所有比 nums[i] 小的元素,都可以作为该序列的倒数第2个数。在这么多个比 nums[i] 小的元素中,以哪个数结尾的最大递增子序列更大,就选这个数作为倒数第2个数。也即:
dp[i] = max{dp[j]+1, dp[i]} 其中: j = 0,1,2,...i-1
代码如下:
public int lengthOfLIS(int[] nums) {
//dp[i] 代表 nums[0,1...i] 0到i 个元素的最长递增子序列的长度
int[] dp = new int[nums.length];
//nums数组 第0个元素的最长递增子序列就是它自己, 因为长度为1
dp[0] = 1;
for (int i = 1; i < nums.length; i++) {
//why?
dp[i] = 1;
//遍历 [0... i-1] 元素
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
//说明针对位置 j 的元素, 可以将第 i 个元素加入, 去形成递增子序列, 但是 nums[j] 和 nums[i] 能不能形成最长子序列呢?答案是不一定
//求解 dp[i], 需要知道 dp[0] dp[1] ... dp[i-1], 找出最大的那个 dp[j]
//最大的那个 dp[j] 可以作为: dp[i] 代表的最大递增子序列的 倒数第2个元素
dp[i] = Math.max(dp[j] + 1, dp[i]);
}
}
}
int maxIndex = findSeqLenMaxIndex(dp);
return dp[maxIndex];
}
private int findSeqLenMaxIndex(int[] arr) {
int maxIndex = arr.length - 1;
int maxEle = arr[arr.length - 1];
for (int i = arr.length - 1; i >= 0; i--) {
if (arr[i] > maxEle) {
maxIndex = i;
maxEle = arr[i];
}
}
//数组 arr 中最大元素的 下标
return maxIndex;
}
由于 dp 数组记录的是最长递增子序列的长度。我们也可以进一步得到:最长子序列。
根据上面示例数组 [0,3,1,6,2,2,7] 的最长子序列长度求得的 dp 数组是:dp=[1, 1, 1, 2, 2, 3, 4, 4]
我们从 dp 数组的最后一个元素开始,寻找 dp 数组中最大值,然后针对最大值所在的下标 maxIndex,往前遍历,如果:
nums[j] < nums[maxIndex] && dp[j] == dp[i]-1,说明 nums[j] 可以作为最长递增子序列的倒数第2个数。
生成最长递增子序列的代码如下:
private int[] generateLIS(int[] nums, int[] dp) {
int seqLenMaxIndex = findSeqLenMaxIndex(dp);
//最大递增子序列的 长度
int seqLen = dp[seqLenMaxIndex];
//存储 最大递增子序列的 元素
int[] maxSeqArr = new int[seqLen];
//最长递增子序列 的最后一个元素
maxSeqArr[--seqLen] = nums[seqLenMaxIndex];
for (int i = seqLenMaxIndex - 1; i >= 0; i--) {
if (nums[i] < nums[seqLenMaxIndex] && dp[i] == dp[seqLenMaxIndex] - 1) {
maxSeqArr[--seqLen] = nums[i];
//最长递增子序列 的前一个最大元素的 下标
seqLenMaxIndex = i;
}
}
return maxSeqArr;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端