一、题目

给你一个整数数组 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(n2) 的解决方案吗?
你能将算法的时间复杂度降低到 O(n log(n)) 吗?

二、思路
  • dp[i]:代表以i索引结尾的最长递增子序列的长度,它可以由前面i-1个状态转移而来,计算出最大的转移长度
  • 优化:使用二分进行优化,每一个数在dp数组里面查找出大于等于的那个数的位置,替换掉该位置的数,最后返回dp的长度即可。此时的dp[i]表示长度为i的上升子序列的末尾元素的最小值。
  • dp[i]维护了上升子序列末尾元素最小,当遇到有一个值,比某个位置j的元素要小,代表它可以替代它成为长度为j的上升子序列的最后一个元素,这样就保证了上升子序列的上升速度更慢,即最后一个元素的值更小。
三、代码

class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size(), ans = 1;
vector<int> dp(1, nums[0]);
for (int i = 1; i < n; i++) {
int idx = lower_bound(dp.begin(), dp.end(), nums[i]) - dp.begin();
if (idx > dp.size() - 1) dp.push_back(nums[i]);
else dp[idx] = nums[i];
}
return dp.size();
}
};

class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size(), ans = 1;
vector<int> dp(n, 1);
for (int i = 0; i < n; i++) {
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;
}
};