练习一 : 最长递增子序列

java
class Solution {
public int lengthOfLIS(int[] nums) {
int n = nums.length;
int[] dp = new int[n];
// 初始化:每个元素至少是长度为1的子序列
Arrays.fill(dp, 1);
int maxLen = 1;
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
// 如果nums[i]可以接在nums[j]后面
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
maxLen = Math.max(maxLen, dp[i]);
}
return maxLen;
}
}
算法原理
-
DP 状态定义 :
dp[i]:以 nums [i] 这个数字结尾的最长递增子序列长度。 -
初始化 :
dp[i] = 1因为每个数字自己单独就是一个长度为 1 的递增子序列。 -
对于每个数
nums[i],看它前面所有的数nums[j]只要nums[j] < nums[i],说明nums[i]可以接在以 j 结尾的序列后面长度就变成:dp[j] + 1
练习二 : 摆动序列

java
class Solution {
public int wiggleMaxLength(int[] nums) {
int n = nums.length;
int[] f = new int[n];
int[] g = new int[n];
int ret = 1;
for(int i = 0;i<n;i++) f[i] = g[i] = 1;
for(int i = 1;i<n;i++){
for(int j = i-1;j>=0;j--){
if(nums[j]<nums[i]){
f[i] = Math.max(f[i],g[j]+1);
}
if(nums[j]>nums[i]){
g[i] = Math.max(g[i],f[j]+1);
}
ret = Math.max(Math.max(f[i],g[i]),ret);
}
}
return ret;
}
}
算法原理
- 状态定义(你用了两个 dp 数组,非常标准)
f[i]:以 nums [i] 结尾,且最后一步是上升(nums [i] > 前一个数)的最长摆动序列长度
g[i]:以 nums [i] 结尾,且最后一步是下降(nums [i] < 前一个数)的最长摆动序列长度 - 初始化 f[i] = g[i] = 1
每个元素自己就是一个长度为 1 的摆动序列。 - 状态转移(你写得完全正确!)
(1)当 nums [j] < nums [i] → 上升 说明i 可以接在 j 的下降序列后面 f[i] = max(f[i], g[j] + 1)
(2)当 nums [j] > nums [i] → 下降 说明i 可以接在 j 的上升序列后面g[i] = max(g[i], f[j] + 1)
摆动序列要求:上升 ↔ 下降 交替
所以:
想上升,必须接在下降结尾后面
想下降,必须接在上升结尾后面 - 答案
遍历过程中 f[i] 和 g[i] 的最大值
练习三 : 最长递增子序列的个数
673. 最长递增子序列的个数 - 力扣(LeetCode)

.
java
class Solution {
public int findNumberOfLIS(int[] nums) {
int n = nums.length;
int[] len = new int[n]; // 以i结尾的最长递增子序列长度
int[] cnt = new int[n]; // 以i结尾的最长递增子序列的个数
// 初始化
for (int i = 0; i < n; i++) {
len[i] = 1;
cnt[i] = 1;
}
int maxLen = 1; // 全局最长长度
// 填dp表
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
if (nums[j] < nums[i]) { // 严格递增
if (len[j] + 1 > len[i]) {
// 找到更长的序列,更新长度和计数
len[i] = len[j] + 1;
cnt[i] = cnt[j];
} else if (len[j] + 1 == len[i]) {
// 长度相同,累加计数
cnt[i] += cnt[j];
}
}
}
// 更新全局最长长度
maxLen = Math.max(maxLen, len[i]);
}
// 统计所有长度等于maxLen的个数之和
int res = 0;
for (int i = 0; i < n; i++) {
if (len[i] == maxLen) {
res += cnt[i];
}
}
return res;
}
}
算法原理
- 状态定义
len[i]:以 nums[i] 结尾的最长递增子序列的长度
cnt[i]:以 nums[i] 结尾的、长度为 len[i] 的最长递增子序列的个数
- 初始化
len[i] = 1:每个元素自身是长度为 1 的子序列
cnt[i] = 1:每个元素自身对应 1 个长度为 1 的子序列
- 状态转移
遍历每个 i,枚举所有 j < i:
若 nums[j] < nums[i](满足严格递增):
若 len[j] + 1 > len[i]:说明找到了更长的子序列,更新 len[i] = len[j] + 1,同时 cnt[i] = cnt[j](个数继承自 j)
若 len[j] + 1 == len[i]:说明找到了长度相同的新子序列,累加计数 cnt[i] += cnt[j]
若 nums[j] >= nums[i]:不满足严格递增,跳过
- 最终结果
先遍历 len 数组,找到全局最长长度 maxLen
再遍历所有 i,累加所有 len[i] == maxLen 的 cnt[i],即为答案