给定一个未经排序的整数数组,找到最长且连续的的递增序列。
示例 1:
输入: [1,3,5,4,7]
输出: 3
解释: 最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为5和7在原数组里被4隔开。
class Solution {
public:
int findLengthOfLCIS(vector<int>& nums) {
int current = 1,ans=1;
int size = nums.size();
if(size==0) return 0;
for(int i=1;i<size;i++)
{
if(nums[i]>nums[i-1])
{
current++;
}
else
{
if(current>=ans){ans=current;}
current = 1;
}
}
if(current>=ans){ans = current;}
return ans;
}
};
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
主要思路:先判断前i个的最大增长子序列,递推公式
dp[i]=max(dp[j])+1,其中0≤j
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = (int)nums.size();
if(n==0) return 0;
vector<int> dp(n,0);
for(int i=0;i<n;i++)
{
dp[i]=1;
for(int j =0;j<i;j++)
{
if(nums[j]<nums[i])
{
dp[i]=max(dp[i],dp[j]+1);
}
}
}
return *max_element(dp.begin(),dp.end());
}
};
解法二:贪心算法+二分查找O(n*log(n))
我们组成子序列的时候,不仅要让这个序列尽可能的长,而且要让子序列中的上升的时候尽可能的缓慢,[2,3]就比[2,5]上升的缓慢,这样就有机会能拼接出更长的上升子序列。
我们用一个数组来保存当前的最长上升子序列,这个数组是严格递增的。
因为是严格递增的,数组中最后一个值nums[max]就是最大值,如果下次再碰到一个数字n,它比num[max]还要大,那么很明显,这个子序列的长度就要+1,并且将数组n添加到数组的末尾。
[2,3,7,8,11,13,18]是目前为止最长的上升子序列,之后如果又碰到了19,或者101,因为他们都大于数组中的最大值18,所以直接将其添加到数组末尾就可以了,同时子序列的长度要+1。
19和101的例子很好理解,但如果下次碰到的数字是6或者12呢?
因为要让子序列上升的尽可能缓慢,那么让[2,5,7…]变成[2,5,6…]更合适,因为后者上升的更缓慢。
同样,将[…8,11,13,18]变成[…8,11,12,18]也是上升的更缓慢一点。
也就是,已知上升子序列[i,i_1,i_2,…,i_n],现在我们在继续遍历的过程中碰到了一个值i_k,这个值是小于i_n的,所以上升子序列的长度还是不变。但是我们需要找到一个位置,将i_k替换掉某个旧的值。
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = (int)nums.size();
int len=1;
if(n==0) return 0;
vector<int> p(n+1,0);
p[len] = nums[0];
for(int i=0;i<n;i++)
{
if(nums[i]>p[len])
{
p[++len]=nums[i];
}
else
{
int l=1,r=len,pos=0;
while(l<=r)//二分查找
{
int mid = (l+r) / 2;
if(p[mid]<nums[i])
{
pos = mid;
l = pos+1;
}
else r=mid-1;
}
p[pos+1] = nums[i];//当没有找到比num小的值的时候,更新第一个数。
}
}
return len;
}
};
给定一个未排序的整数数组,找到最长递增子序列的个数。
示例 1:
输入: [1,3,5,4,7]
输出: 2
解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, 5, 7]。
示例 2:
输入: [2,2,2,2,2]
输出: 5
解释: 最长递增子序列的长度是1,并且存在5个子序列的长度为1,因此输出5。
相比于上面一道题,此题更为复杂,在记录最长子序列的同时,还需要统计最长子序列的个数。思路同上题,我们需要维护一个向量maxLen来存储每个位置下的最长上升子序列,同时还需要一个向量count来记录每个子序列的个数。在代码实现的时候同之前类似利用动态规划,不同的是在nums[j]>nums[i]的时候,我们需要分情况来更新count[i]的值。如果maxLen[j]+1==maxLen[i]的时候,就会多出来count[j]个上升序列是maxLen[i]的序列,因此,count[i] +=count[j];如果maxLen[j]+1>maxLen[i]时,更新maxLen[i] = maxLen[j]+1,此时的count[i] 赋值为count[j]。最后在每次外层循环时候需要判断当前的maxLen[i]是不是最长的上升子序列,如果是,那么就要更新ans +=count[i],如果maxLen[i]大于最长的上升子序列,那么更新ansLen,同时赋值ans = count[i]。最后的ans就是答案。
class Solution {
public:
int findNumberOfLIS(vector<int>& nums) {
int n = nums.size();
if(n<=1) return n;
vector<int> maxLen(n,1);
vector<int> count(n,1);
int ans = 0;
int ansLen = 1;
for(int i = 0;i<n;i++)
{
for(int j=0;j<i;j++)
{
if(nums[j]<nums[i])
{
if(maxLen[j]+1 == maxLen[i])
{
count[i] += count[j];
}
if(maxLen[j]+1 > maxLen[i])
{
maxLen[i] = maxLen[j] + 1;
count[i] = count[j];
}
}
}
if(maxLen[i] == ansLen)
{
ans += count[i];
}
if(maxLen[i] > ansLen)
{
ansLen = maxLen[i];
ans = count[i];
}
}
return ans;
}
};