连续最长增长序列(简单)+最长上升子序列问题(中等)+最长上升子序列的个数(中等)

连续最长增长序列(简单)

题目描述

给定一个未经排序的整数数组,找到最长且连续的的递增序列。

示例 1:

输入: [1,3,5,4,7]
输出: 3
解释: 最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为5和7在原数组里被4隔开。

解决方案:滑动窗口O(N)

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) 吗?

解法一:动态规划O( n 2 n^{2} n2)

主要思路:先判断前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;
    }
};

你可能感兴趣的:(数据结构算法刷题)