题目
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
思路一:时间复杂度 O ( N 2 ) {O(N^2)} O(N2),空间复杂度 O ( N ) {O(N)} O(N)
遍历数组每个元素,如果把数组元素当做子序列起始值,后面添加到序列中的复杂度很高。换个角度想,因为是递增子序列,如果确定子序列的终止元素,可有效降低复杂度。
遍历每个数组元素,将每个元素视作上升序列的终止元素,依次与终止元素前的元素比较,如果比前面的元素大,则以当前元素作为终止元素的上升序列长度为前面元素作为终止元素的长度+1,再取一个最大值作为当前元素的最终结果。其实就是动态规划的思想。
思路二:时间复杂度 O ( N l o g N ) {O(NlogN)} O(NlogN),空间复杂度 O ( l o g N ) {O(logN)} O(logN)
参考详解
贪心算法,上升子序列的终止值越小越好,越小后面可以添加的递增数越多。
新建一个dp数组,存储上升子序列的终止值(希望终止值尽量小),如果下一个数组元素比dp最后一个数大,直接加到dp数组末尾。如果比dp最后一个数小,用这个较小的数覆盖dp数组中合适位置的数(遍历dp,替换掉遇到的第一个比它大的数),继续下次循环。如果和dp最后一个数相等,则不需要操作,继续循环即可。
具体代码如下:
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int len = nums.size();
if (len < 2)
return len;
vector<int> dp;
for(int i = 0;i < len;i++){
if(i == 0)
dp.push_back(nums[i]);
else{
if(nums[i] < dp.back()){
for(int j = 0;j < dp.size();j++){
if(dp[j] > nums[i])
{
dp[j] = nums[i];
break;
}
else if(dp[j] == nums[i])
break;
}
}
else if(nums[i] > dp.back()){
dp.push_back(nums[i]);
}
}
}
return dp.size();
}
};