LeetCode:300 最长上升子序列 O(nlog(n))解法 动态规划/二分搜索

题目

给定一个无序的整数数组,找到其中最长上升子序列的长度。

示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。

说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-increasing-subsequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

动态规划

状态定义

dp[i]表示以下标i结尾的最长上升子序列长度

状态转移:在前面找最长的,然后+1即可

dp[i] = max(dp[0], dp[1], ... dp[i-1]) + 1
class Solution {
public:
    int lengthOfLIS(vector<int>& nums)
    {
        if(nums.size()==0) return 0;
        vector<int> dp(nums.size(), 1);
        for(int i=1; i<nums.size(); i++)
            for(int j=0; j<i; j++)
                if(nums[j]<nums[i]) dp[i]=max(dp[i], dp[j]+1);
        return dp[max_element(dp.begin(), dp.end())-dp.begin()];
    }
};

O(nlog(n))优化:

维护一个升序的数组,总是尽可能的去拓展它的长度,即遍历nums,将nums[i]二分地搜索插入的位置

  • 如果nums[i]比任何一个元素都大,那么在结尾插入nums[i]
  • 如果nums[i]不是最大的,那么用nums[i]替换掉第一个比它大的元素
  • 最后返回序列的长度就是答案(虽然答案不是这个序列但是长度一定是正确的)

解释:因为同上面的动态规划,每次我们希望找到比i要小,但是以其结尾的最长上升子序列最长的下标j,那么我们可以维护一个数组,升序的存储数组中的元素,以使用二分来快速查找,这个序列可以代表【我们已知的最长上升子序列】

那么nums[i]如果是最大的,就可以拓展已知的序列

如果nums[i]不是最大的,序列无法拓展,但是我们可以尝试将序列中第一个比nums[i]大的元素换为nums[i],序列仍然保持升序,但是序列被拓展的可能提高了,因为序列里的元素变小了(尤其是替换的是最后一个元素时)

用 lower_bound 找第一个大于等于 nums[i]的元素

class Solution {
public:
    int lengthOfLIS(vector<int>& nums)
    {
        if(nums.size()<2) return nums.size();
        vector<int> seq{nums[0]};
        for(int i=1; i<nums.size(); i++)
        {
            int idx = lower_bound(seq.begin(), seq.end(), nums[i])-seq.begin();
            if(idx==seq.size()) seq.push_back(nums[i]);
            else seq[idx]=nums[i];
        }
        return seq.size();
    }
};
发布了238 篇原创文章 · 获赞 7 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44176696/article/details/104859447