题目描述
题目链接
给你一个整数数组 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
题目分析
这算是一个比较经典的问题了,很多其它问题都可以由它衍生出来,在此记录一下两种常见解法。
方法1 动态规划
设dp[i]表示以nums[i]结尾的最长的递增子序列的长度,则有递推公式
d
p
[
i
]
=
m
a
x
(
d
p
[
i
]
,
d
p
[
j
]
+
1
)
i
f
n
u
m
s
[
i
]
>
n
u
m
s
[
j
]
f
o
r
0
≤
j
<
i
dp[i] = max(dp[i], dp[j] + 1) if nums[i] > nums[j] for 0\leq j < i
dp[i]=max(dp[i],dp[j]+1)ifnums[i]>nums[j]for0≤j<i
遍历过程中用ans记录出现过的最大值,最后返回ans即可。
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int dp[nums.size()];
int ans = 1;
for(int i = 0; i < nums.size(); i++){
dp[i] = 1;
for(int j = 0; j < i; j++){
if(nums[j] < nums[i]){
dp[i] = max(dp[i], dp[j] + 1);
}
}
ans = max(ans, dp[i]);
}
return ans;
}
};
方法2 二分查找
我们用length[i]表示所有长度为i的递增子序列的最后一个元素最小值,可以证明length中的元素是严格递增的(反证法)。遍历nums,遍历到i时,如果nums[i]比当前length的最后一个元素还要大,就将这个元素加入length的后面,否则二分查找length中的值,替换掉严格小于nums[i]的最大元素。
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int length[nums.size() + 1];
memset(length, 0, sizeof(length));
int ans = 1;
length[1] = nums[0];
for(int i = 1; i < nums.size(); i++){
if(nums[i] > length[ans]){
length[++ans] = nums[i];
}
else{
//另一种写法
// int l = 1, r = ans;
// while(l < r){
// int m = (l + r) >> 1;
// if(length[m] < nums[i]){
// l = m;
// }
// else{
// r = m;
// }
// if((length[l] < nums[i])&&(length[r] >= nums[i])&&(l == r - 1)){
// break;
// }
// }
// length[r] = nums[i];
int l = 1, r = ans, loca = 0;
while(l <= r){
int m = (l + r) >> 1;
if(length[m] < nums[i]){
loca = m;
l = m + 1;
}
else{
r = m - 1;
}
}
length[loca + 1] = nums[i];
}
}
return ans;
}
};