题目
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-increasing-subsequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
动态规划
状态定义
dp[i]表示以下标i结尾的最长上升子序列长度
状态转移:在前面找最长的,然后+1即可
dp[i] = max(dp[0], dp[1], ... dp[i-1]) + 1
class Solution {
public:
int lengthOfLIS(vector<int>& nums)
{
if(nums.size()==0) return 0;
vector<int> dp(nums.size(), 1);
for(int i=1; i<nums.size(); i++)
for(int j=0; j<i; j++)
if(nums[j]<nums[i]) dp[i]=max(dp[i], dp[j]+1);
return dp[max_element(dp.begin(), dp.end())-dp.begin()];
}
};
O(nlog(n))优化:
维护一个升序的数组,总是尽可能的去拓展它的长度,即遍历nums,将nums[i]
二分地搜索插入的位置
- 如果
nums[i]
比任何一个元素都大,那么在结尾插入nums[i]
- 如果
nums[i]
不是最大的,那么用nums[i]
替换掉第一个比它大的元素 - 最后返回序列的长度就是答案(虽然答案不是这个序列但是长度一定是正确的)
解释:因为同上面的动态规划,每次我们希望找到比i
要小,但是以其结尾的最长上升子序列最长的下标j
,那么我们可以维护一个数组,升序的存储数组中的元素,以使用二分来快速查找,这个序列可以代表【我们已知的最长上升子序列】
那么nums[i]
如果是最大的,就可以拓展已知的序列
如果nums[i]
不是最大的,序列无法拓展,但是我们可以尝试将序列中第一个比nums[i]
大的元素换为nums[i]
,序列仍然保持升序,但是序列被拓展的可能提高了,因为序列里的元素变小了(尤其是替换的是最后一个元素时)
用 lower_bound 找第一个大于等于 nums[i]
的元素
class Solution {
public:
int lengthOfLIS(vector<int>& nums)
{
if(nums.size()<2) return nums.size();
vector<int> seq{nums[0]};
for(int i=1; i<nums.size(); i++)
{
int idx = lower_bound(seq.begin(), seq.end(), nums[i])-seq.begin();
if(idx==seq.size()) seq.push_back(nums[i]);
else seq[idx]=nums[i];
}
return seq.size();
}
};