「这是我参与2022首次更文挑战的第9天,活动详情查看:2022首次更文挑战」。
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
一、分析
子序列:可以不连续
[4,1,3,2,3,9,5,6]:选 1,2,3,5,6
dp[i]:子序列必须以i位置的数结尾的情况下,最长递增子序列是多长
max(dp[i])
O(N²) -> dp
-
[4,1,3,2,3,9,5,6] => [1,1,2,2,3,4,4,5]
-
当前位置与左边一次比较,从小的dp中选出dp最大值,当前位置的值 = 左边dp最大值 + 1
O(N * logN):最优解
- i位置,左边遍历一下找到比我小的最大的dp值 + 1,这样时间复杂度还是O(N²)
- 找到i位置dp值不通过遍历,二分O(N * logN)
- end[i]:目前所有长度为i+1的递增子序列最小结尾是end[i]
有效区
二、实现
// 时间复杂度O(N²)
public static int lengthOfLIS(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
int[] dp = new int[arr.length];
dp[0] = 1;
int maxans = 1;
for (int i = 1; i < arr.length; i++) {
dp[i] = 1;
for (int j = 0; j < i; j++) {
if (arr[i] > arr[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
maxans = Math.max(maxans, dp[i]);
}
return maxans;
}
// 时间复杂度O(N*logN)
public static int lengthOfLIS(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
int[] ends = new int[arr.length];
ends[0] = arr[0];
int right = 0;
int l = 0;
int r = 0;
int m = 0;
int max = 1;
for (int i = 1; i < arr.length; i++) {
l = 0;
r = right;
while (l <= r) {
m = (l + r) / 2;
if (arr[i] > ends[m]) {
l = m + 1;
} else {
r = m - 1;
}
}
right = Math.max(right, l);
ends[l] = arr[i];
max = Math.max(max, l + 1);
}
return max;
}
三、总结
模仿(先人的东西不是那么好想的!)
慢慢培养敏感度
扩展:俄罗斯套娃问题
(长度,高度):先长度从小到大排序,再高度从大到小排序
(7,2) (5,6) (1,3) (1,5) (1,2) (5,4) (7,6)
[1,5] [1,3] [1,2] [5,6] [5,4] [7,6] [7,2]
[5,3,2,6,4,6,2]:最长子序列就是能套几层