leetcode 300 最长上升子序列的长度
给定一个整型数据串,求其严格单调递增的最长的子序列长度。
两种思路,第一种思路dp,设dp[i]为以第i个数字作为结束元素的子序列的最长的长度,那么这个值只会与其前i-1个元素相关,递推式为:
dp[i] = max{dp[k]}+1,1 <= k <= i-1 && nums[k] < nums[i]
时间复杂度为O(n^2)
代码:
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
if(nums.size()==1 && nums[0] ==0) return 1;
int dp[2505];
int m1 = 0;
memset(dp,0,sizeof(dp));
dp[0] = 1;
for(int i = 0;i < nums.size()-1;i++){
int m2 = 0;
for(int j = 0;j <= i;j++){
if(dp[j] > m2 && nums[j] < nums[i+1]){
m2 = dp[j];
}
}
dp[i+1] = m2+1;
if(dp[i+1] > m1) m1 = dp[i+1];
}
return m1;
}
};
200 ms 10.1MB
第二种思路,贪心+二分。设d[len]为长度为len的上升子序列结尾元素的值,我们从左到右扫描输入序列,对于每个当前扫描的元素,处理数组d的更新,具体更新如下:
我们已经知道,假如当前扫描的元素是第k个,那么我们的数组d(更新后的)就是前k个元素中各个长度的上升子序列,对于这k个元素的各个上升子序列,我们只关心其结尾元素谁大谁小,而不关心谁在前面谁在后面,因为后续在继续处理的时候,处理的是第k+1个元素及以后的元素,我们只要求这个结尾的元素尽可能小就可以了,这样后续的元素添加到当前子序列末尾的可能性更大一些。因此数组d中每一个元素是唯一的,其选取服从贪心规则。
如何更新数组d呢?我们要找到第k个元素所能连接在后面的最长的子序列,然后将第k个元素连在它的后面,即找到不大于nums[k]的下标len最大的d中元素d[len],然后将d[len+1]更新为较小的nums[k]。以上过程中,更新时d后面的元素总是要大于d前面的元素,因此d是严格单调递增的,这样在查找过程时可以直接二分。二分可以给出某个不在序列中的元素应该被插入的位置,刚好适合本题的情况。
时间复杂度为O(nlogn)
代码:
class Solution {
vector<int>& num;
int d[2005];
int max_ = -1;
public:
int binSearch(int left,int right,int target){
while (left <= right)
{
int mid = left + (right - left)/2;
if(num[mid] == target){
return -1;
}
if(num[mid] > target){
right = mid - 1;
}else if(num[mid] < target){
left = mid + 1;
}
}
return left;
}
public:
int findNumberOfLIS(vector<int>& nums) {
num = nums;
memset(d,0,sizeof(d));
for(int i = 0;i < nums.size();i++){
int tmp = binSearch(0,i,nums[i]);
if(tmp != -1){
d[tmp+1] = nums[i];
if(tmp+1 > max_) max_ = tmp+1;
}
}
}
};
8ms 10.3MB