【LeetCode】300. 最长递增子序列

300. 最长递增子序列(中等)

在这里插入图片描述
【LeetCode】300. 最长递增子序列_第1张图片
在这里插入图片描述

方法一:动态规划

  1. 思路

    • 通常来说,子序列不要求连续,而子数组子字符串必须连续;
    • 对于子序列问题,第一种动态规划方法是,定义 dp 数组,其中 dp[i] 表示以 i 结尾的子序列的性质。在处理好每个位置后,统计一遍各个位置的结果即可得到题目要求的结果。
    • 在本题中, dp[i] 可以表示为以 i 结尾的、最长子序列长度。对于每个位置 i ,如果其之前的某个位置 j 所对应的数字小于位置 i 所对应的数字 ,则我们可以获得一个以 i 结尾、长度为 dp[j] + 1 的子序列。为了遍历所有情况,我们需要对 i 和 j 进行两层循环,其时间复杂度为 O(n2)。
  2. 代码

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

    • 我一开始的想法是将 dp[i] 表示为前 i 位最长递增子序列的长度,但是发现并不好算。

方法二:二分查找

  1. 思路

    • 本题还可以使用二分查找将时间复杂度降为 O(n logn)。
    • 定义 dp数组,其中 dp[k] 表示存储长度为 k+1 的最长递增子序列的最后一个数字
    • 遍历每一个位置 i ,如果对应的数字大于 dp 数组中所有数字的值,那么就把他放在 dp 数组的尾部,表示最长递增子序列长度+1;如果这个数字介于当前dp数组最小值和最大值之间,那么将最大值更新为当前数组,使得之后构成递增序列的可能性加大。
    • 根据这个方法维护 dp 数组永远是递增的,因此可以用二分查找加速搜索。
    • 然而,使用这个方法需要注意, dp数组的最终形式不一定是合法的排列形式,但最优解一定出现在遍历的过程中。
  2. 代码

    class Solution {
    public:
        int lengthOfLIS(vector<int>& nums) {
            int ans = 0;
            vector<int> dp;
            dp.push_back(nums[0]);
            for(int i=1; i<nums.size(); ++i){
                if(dp.back() < nums[i]){
                    dp.push_back(nums[i]);
                }
                else{
                    // 通过二分查找,返回第一次出现大于等于 nums[i] 的地址
                    auto itr = lower_bound(dp.begin(), dp.end(), nums[i]);
                    *itr = nums[i];
                }
            }
            return dp.size();
        }
    };
    
  3. 收获

    • 这个思路还挺新奇的,学习了 lower_bound 函数的用法 lower_bound。

你可能感兴趣的:(LeetCode刷题,leetcode,算法,动态规划)