最长递增子序列的个数
难度:
标签:
题目描述
代码结果
运行时间: 54 ms, 内存: 16.6 MB
/*
* 题目思路:
* 1. 使用动态规划结合 Java Stream 来实现。
* 2. 定义两个数组:lengths 和 counts。
* - lengths[i] 表示以 nums[i] 结尾的最长递增子序列的长度。
* - counts[i] 表示以 nums[i] 结尾的最长递增子序列的个数。
* 3. 对于每个 nums[i],使用 Stream 过滤并计算能够形成递增序列的元素。
* 4. 最后通过 Stream 找到最大长度,并计算对应的子序列个数。
*/
import java.util.Arrays;
import java.util.stream.IntStream;
public class Solution {
public int findNumberOfLIS(int[] nums) {
if (nums == null || nums.length == 0) return 0;
int n = nums.length;
int[] lengths = new int[n];
int[] counts = new int[n];
Arrays.fill(counts, 1);
int maxLength = 0;
IntStream.range(0, n).forEach(i -> {
IntStream.range(0, i).forEach(j -> {
if (nums[j] < nums[i]) {
if (lengths[j] + 1 > lengths[i]) {
lengths[i] = lengths[j] + 1;
counts[i] = counts[j];
} else if (lengths[j] + 1 == lengths[i]) {
counts[i] += counts[j];
}
}
});
maxLength = Math.max(maxLength, lengths[i]);
});
return IntStream.range(0, n)
.filter(i -> lengths[i] == maxLength)
.map(i -> counts[i])
.sum();
}
}
解释
方法:
该题解使用动态规划来解决最长递增子序列的个数问题。具体思路如下:
1. 使用两个列表dp和count来记录状态。其中dp[i]表示长度为i+1的递增子序列的结尾元素列表,count[i]表示以dp[i]中每个元素结尾的递增子序列个数累计值列表。
2. 遍历给定数组nums中的每个元素num:
a. 二分查找dp中第一个大于等于num的元素位置i。
b. 如果i等于dp的长度,说明num可以接在当前最长递增子序列之后,形成一个新的最长子序列,因此将num添加到dp中,并在count中记录以num结尾的子序列个数为1。
c. 如果i小于dp的长度,说明num可以替换dp[i]中的元素,形成一个新的长度为i+1的递增子序列。此时在dp[i]中二分查找第一个大于num的元素位置k,然后将num添加到dp[i]的末尾,并在count[i]中累加count[i-1][-1] - count[i-1][k],表示以num结尾的递增子序列个数。
3. 最终返回count[-1][-1],即以dp中最后一个元素结尾的递增子序列总数。
时间复杂度:
O(n log n)
空间复杂度:
O(n)
代码细节讲解
🦆
在算法中,为何选择使用两个数组dp和count,它们各自的作用和存储的数据类型是什么?
▷🦆
在二分查找中,为什么要找到dp中第一个大于等于num的元素位置,这样做的目的是什么?
▷🦆
在更新count数组时,表达式`count[i-1][-1] - count[i-1][k]`是如何计算得到的,它代表了什么意义?
▷🦆
如果nums数组中存在重复元素,此算法如何处理?请解释算法对于示例2的处理过程。
▷相关问题
最长递增子序列
给你一个整数数组 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))
吗?
最长连续递增序列
给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。
连续递增的子序列 可以由两个下标 l
和 r
(l < r
)确定,如果对于每个 l <= i < r
,都有 nums[i] < nums[i + 1]
,那么子序列 [nums[l], nums[l + 1], ..., nums[r - 1], nums[r]]
就是连续递增子序列。
示例 1:
输入:nums = [1,3,5,4,7] 输出:3 解释:最长连续递增序列是 [1,3,5], 长度为3。 尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为 5 和 7 在原数组里被 4 隔开。
示例 2:
输入:nums = [2,2,2,2,2] 输出:1 解释:最长连续递增序列是 [2], 长度为1。
提示:
1 <= nums.length <= 104
-109 <= nums[i] <= 109