给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
方法一:这是非常经典的动态规划问题。(时间复杂度O(n2),额外空间复杂度O(n))
class Solution {
public:
int lengthOfLIS(vector& nums) {
int result = 0;
int numsSize = nums.size();
vector dp(numsSize, 1);//dp[i]表示以nums[i]结尾的最长上升子序列的长度
//对结尾节点进行穷举
for (int nowEnd = 0; nowEnd < numsSize; ++nowEnd) {
//对结尾节点的最适合的前一个节点进行寻找
for (int lastEnd = 0; lastEnd < nowEnd; ++lastEnd) {
//如果前面的节点结尾的尾部值小于当前节点,说明nowEnd能够作为lastEnd的新结尾
//dp[lastEnd] + 1 > dp[nowEnd]用于判断是否增长了nowEnd结尾的最长上升子序列
if (nums[lastEnd] < nums[nowEnd] && dp[lastEnd] + 1 > dp[nowEnd]) {
dp[nowEnd] = dp[lastEnd] + 1;
}
}
result = max(result, dp[nowEnd]);//更新结果
}
return result;
}
};
方法二:分治法(二分搜索)。(时间复杂度O(n log2n),额外空间复杂度O(n))
给nums[]维护一个递增序列res[],这个序列在保持递增的情况下,不断减小相应元素的值,以使得能有更多的元素能push_back(),从而增大序列的长度。
举个例子:nums=[10,9,2,5,3,7,101,18],该序列的更新过程 …->res[2] = {2, 5}->res[2] = {2, 3}->res[3] = {2, 3, 7}->…
lower_bound(begin, end, value)会返回一个不小于value元素的迭代器。
class Solution {
public:
int lengthOfLIS(vector& nums) {
vector res;
for (int i = 0; i < nums.size(); ++i) {
auto iter = lower_bound(res.begin(), res.end(), nums[i]);//寻找第一个不小于nums[i]的元素
//只有在res后面放入元素才会更改结果
if (iter == res.end()) {//如果是end,说明res中所有元素都小于nums[i],这个元素直接放入
res.push_back(nums[i]);
}
else {
//修改操作不会影响结果,把这个大的元素修改为小的元素是为了后面可能出现更长的上升子序列
*iter = nums[i]; //更新
}
}
return res.size();
}
};