leetcode 300. 最长上升子序列
问题描述
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
思路一(动态规划)
设dp[i]代表以[nums[0],...,nums[i-1]]的最长上升子序列长度,此时时间复杂度O(N^2),空间复杂度O(N).
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
if(n < 2)return n;
int i,j,val,maxnum=-1;
vector<int> dp(n,1);//dp[i]代表以[nums[0],...,nums[i-1]]的最长上升子序列长度
for(i = 0; i < n; i++)
{
val = nums[i];
for(j = 0; j < i; j++)
{
if(nums[j] < val)
{
dp[i] = max(dp[i],dp[j]+1);//nums[i]可以接在nums[i]最长子序列后面形成新的最长子序列
}
}
if(dp[i] > maxnum)maxnum = dp[i];
}
return maxnum;
}
};
结果
执行用时 :56 ms, 在所有 cpp 提交中击败了51.26%的用户
内存消耗 :8.7 MB, 在所有 cpp 提交中击败了15.03%的用户
思路二(二分查找)
新建数组 dp,用于保存最长上升子序列。对原序列进行遍历,将每位元素二分插入 dp 中。
如果 dp 中元素都比它小,将它插到最后;否则,用它覆盖掉比它大的元素中最小的那个。
总之,思想就是让 cell 中存储比较小的元素。这样,cell 未必是真实的最长上升子序列,但长度是对的。
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
if(n < 2)return n;
int i,end = 0,left,right,middle;
vector<int> dp(n,INT_MIN);
dp[0] = nums[0];
for(i = 1; i < n; i++)
{
if(nums[i] > dp[end])
{
dp[++end]=nums[i];
}
else{
left = 0;right = end;
while(left < right)
{
middle = left + (right - left)/2;
if(dp[middle] >= nums[i]){
right = middle ;
}
else{
left = middle + 1;
}
}
dp[right] = nums[i];
}
}
return end+1;
}
};
结果:
执行用时 :4 ms, 在所有 cpp 提交中击败了96.37%的用户
内存消耗 :8.5 MB, 在所有 cpp 提交中击败了71.57%的用户
最外层循环为O(N),内层为二分查找为O(log(N)),因此时间复杂度总计为O(Nlog(N)),空间复杂度为O(N)。