力扣300,最长递增子序列
题目描述
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
输入输出样例
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
输入:nums = [0,1,0,3,2,3]
输出:4
输入:nums = [7,7,7,7,7,7,7]
输出:1
进阶
- 你能将算法的时间复杂度降低到
O(n log(n))
吗?
解法一:动态规划O(N^2)
//使用动态规划的思想,时间复杂度为O(n^2)
//状态转移方程为dp[i]=max(dp[j])+1,0<=j<i&&nums[j]<nums[i]
int lengthOfLIS(vector<int>&nums)
{
if(nums.empty())
{
return 0;
}
int length=nums.size();
//建立动态规划数组
vector<int>dp(length);
//初始化
dp[0]=1;
for(int i=1;i<length;i++)
{
dp[i]=1;
for(int j=0;j<i;j++)
{
if(nums[j]<nums[i])
{
dp[i]=max(dp[j]+1,dp[i]);
}
}
}
int maxSeq=*max_element(dp.begin(),dp.end());
return maxSeq;
}
解法二,贪心+二分查找O(NlogN)
//使用二分查找+贪心算法实现
//时间复杂度为O(NlogN)
int lengthOfLIS2(vector<int>&nums)
{
int length=nums.size();
if(nums.empty())
{
return 0;
}
vector<int>tail;
//初始化
tail.push_back(nums[0]);
//end 表示状态的位置
int end=0;
for(int i=0;i<length;i++)
{
//当下一个数值要比状态数组的末尾要大时,更新状态转移数组
if(nums[i]>tail[end])
{
tail.push_back(nums[i]);
end++;
}
else{
//由于下一个数值比状态数组的末尾要小,则需要将更小的那个进行替换
//因为这是确保状态转移维护的是最小的序列,以便后续的值的插入
int left=0;
int right=end;
//使用二分查找对状态数组进行更新
while(left<right)
{
int mid=(left+right)/2;
if(tail[mid]<nums[i])
{
left=mid+1;
}
else{
right=mid;
}
}
//更新状态数组的值
tail[left]=nums[i];
}
}
return end+1;
}