动态规划应用–最长递增子序列 LeetCode 300[通俗易懂]

动态规划应用–最长递增子序列 LeetCode 300[通俗易懂]文章目录1.问题描述2.解题思路2.1回溯法求解2.2动态规划1.问题描述有一个数字序列包含n个不同的数字,如何求出这个序列中的最长递增子序列长度?比如2,9,3,6,5,1,7这样一组数字序列,它的最长递增子序列就是2,3,5,7,所以最长递增子序列的长度是4。2.解题思路2.1回溯法求解/***@description:最长递增子序列*@author:m…

大家好,又见面了,我是你们的朋友全栈君。

1. 问题描述

有一个数字序列包含n个不同的数字,如何求出这个序列中的最长递增子序列长度?比如2,9,3,6,5,1,7这样一组数字序列,它的最长递增子序列就是2,3,5,7,所以最长递增子序列的长度是4。
https://leetcode-cn.com/problems/longest-increasing-subsequence/

2. 解题思路

2.1 动态规划

  • 假设在包含 i-1 下标数字时的最大递增子序列长度为 maxLen(i-1),那么下标为 i 时的 maxLen(i)需要考虑前面所有的状态,
  • 如果 a[j] < a[i] (0 <= j < i),则 maxlen[i] = max(maxlen[j]+1 | (0 <= j < i));
  • 如果 a[j] >= a[i] (0 <= j < i),则 maxlen[i] = 1;

借一张动图说明
在这里插入图片描述
在这里插入图片描述

class Solution 
{ 
   
public:
    int lengthOfLIS(vector<int>& nums) 
    { 
   
        int n = nums.size();
        if(n == 0)
            return 0;
        int maxlen[n], ans;
        int i, j;
        for(i = 0; i < n; ++i)
            maxlen[i] = 1;//至少为1,自己
        for(i = 1; i < n; ++i)
        { 
   
        	ans = 1;
            for(j = 0; j < i; ++j)
            { 
   
            	if(nums[i] > nums[j] && maxlen[j]+1 > ans)
            	{ 
   
            		ans = maxlen[j]+1;
            		maxlen[i] = ans;
            	} 
        	}
        }
        for(ans = 1, i = 0; i < n; ++i)
        { 
   
        	if(maxlen[i] > ans)//取最大值
        		ans = maxlen[i];
        }
        return ans;
    }
};
class Solution { 
   	//2020.3.14
public:
    int lengthOfLIS(vector<int>& nums) { 
   
        if(nums.size() == 0)
            return 0;
        int i, j, n = nums.size(),maxlen = 1;
        vector<int> dp(n,1);
        for(i = 1; i < n; ++i)
        { 
   
            for(j = i-1; j >= 0; --j)
            { 
   
                if(nums[i] > nums[j])
                    dp[i] = max(dp[i], dp[j]+1);
            }
            maxlen = max(maxlen, dp[i]);
        }
        return maxlen;
    }  
};

2.2 二分查找

  • 参考官方的解答
  • dp[i] 表示长度为 i+1 的子序的最后一个元素的 最小数值
  • 遍历每个 nums[i],找到其在dp数组中的位置(大于等于 nums[i] 的第一个数),将他替换成较小的

以输入序列 [0, 8, 4, 12, 2] 为例:

第一步插入 0,dp = [0]

第二步插入 8,dp = [0, 8]

第三步插入 4,dp = [0, 4]

第四步插入 12,dp = [0, 4, 12]

第五步插入 22,dp = [0, 2, 12]

class Solution { 
   
public:
    int lengthOfLIS(vector<int>& nums) { 
   
        if(nums.size() == 0)
            return 0;
        int i, l, r, n = nums.size(), maxlen = 1, idx;
        vector<int> dp(n);
        dp[0] = nums[0];
        for(i = 1; i < n; ++i)//遍历每个数
        { 
   
            l = 0, r = maxlen-1;
            idx = bs(dp,l,maxlen,nums[i],maxlen);
			//二分查找nums[i] 在dp中的位置
            if(idx == maxlen)//nums[i] 是最大的
            { 
   
                dp[idx] = nums[i];
                maxlen++;
            }
            else//不是最大的,更新 dp[i] 里的数为较小的
                dp[idx] = min(dp[idx], nums[i]);
        }
        return maxlen;
    }  

    int bs(vector<int> &dp, int l, int r, int& target, int& maxlen)
    { 
   	//二分查找nums[i] 在dp中的位置, 第一个大于等于 nums[i] 的
        int mid;
        while(l <= r)
        { 
   
            mid = l + ((r-l)>>1);
            if(dp[mid] < target)
                l = mid+1;
            else
            { 
   
                if(mid == 0 || dp[mid-1] < target)
                    return mid;
                else
                    r = mid-1;
            }
        }
        return maxlen;//没有找到,nums[i] 最大,放最后
    }
};
  • 基于上面的想法,直接用 treeset 可以简化代码
class Solution { 
   
public:
    int lengthOfLIS(vector<int>& nums) { 
   
        if(nums.size() == 0)
            return 0;
        set<int> s;
        for(auto& n : nums)
        { 
   
            if(s.count(n))
                continue;
            else
            { 
   
                auto it = s.upper_bound(n);//n的上界
                if(it == s.end())//没有比我大的
                    s.insert(n);
                else//有比我大的
                { 
   
                    s.erase(it);//删除比我大的
                    s.insert(n);//换成我
                }
            }
        }
        return s.size();
    }
};

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/137616.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • r语言ggplot回归分析图_R如何导入数据

    r语言ggplot回归分析图_R如何导入数据#第一章R基础install.packages(c("ggplot2","gcookbook"))library(ggplot2)library(gcookbook)#加载分隔符式的文本文件#data&lt;-read.csv("datafile.csv")#data&lt;-read.csv("datafile.csv",header=FALSE)#手动为列名赋值#name…

    2022年10月3日
    0
  • 彻底了解DVD:从入门到精通(三)[转]「建议收藏」

    彻底了解DVD:从入门到精通(三)[转]「建议收藏」第七章 正版与盗版    这一章有两大问题的预备知识需要交待。  第一大问题是最最敏感的问题,也是本文无法在正规刊物上发表的致命原因,对于DVD,我们应该收藏正版还是D版?  第二个问题是,如果要收藏D版,要注意哪些问题。  谈到收藏,先要解释几个常用名词(第九章会有更详细的解释):  D版:盗版,成为这个敏感词的代称。  D商:有两个意思,一是指盗版商,也叫DS,二是泛指碟商。大…

    2022年7月11日
    16
  • 菜鸟教程java的list_Java菜鸟教程

    菜鸟教程java的list_Java菜鸟教程分阶段进阶教学+阶段考评让学习无死角因为考虑学员基础水平参差不齐,所以动力节点的课程安排对学员进行科学细致的划分,整个教学安排共分两大部分即:基础部分和就业部分,基础部分课程由教学总监定制最适合零基础入门的课程大纲;就业部分课程由教研部实地探访名企如百度、京东、新浪等企业,将最前沿的技术引入到课堂,同时又根据就业课程的深度不同划分为7个阶段,每个阶段都有不同的技术侧重点,层层深入。纵观来看,动力…

    2022年6月13日
    27
  • docker下载安装教程_mac docker 性能

    docker下载安装教程_mac docker 性能前言Docker提供轻量的虚拟化,你能够从Docker获得一个额外抽象层,你能够在单台机器上运行多个Docker微容器,而每个微容器里都有一个微服务或独立应用,例如你可以将Tomcat运行在一个D

    2022年7月28日
    26
  • PyCharm 2021.10.3 激活码(JetBrains全家桶)[通俗易懂]

    (PyCharm 2021.10.3 激活码)2021最新分享一个能用的的激活码出来,希望能帮到需要激活的朋友。目前这个是能用的,但是用的人多了之后也会失效,会不定时更新的,大家持续关注此网站~IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.html…

    2022年3月27日
    53
  • Laravel 单设备登录

    Laravel 单设备登录

    2021年10月27日
    28

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号