动态规划应用–最长递增子序列 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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • python的dropna_python–data.dropna[通俗易懂]

    python的dropna_python–data.dropna[通俗易懂]读取csv文件data=pd.read_csv(“”)1、删除全为空值的行或列data=data.dropna(axis=0,how=’all’)#行data=data.dropna(axis=1,how=’all’)#列2、删除含有空值的行或列data=data.dropna(axis=0,how=’any’)#行data=data.dropna(axis=1,how=’an…

    2026年1月14日
    3
  • 《chkconfig命令》-linux命令五分钟系列之四

    《chkconfig命令》-linux命令五分钟系列之四

    2021年9月8日
    62
  • java CAS详解[通俗易懂]

    java CAS详解[通俗易懂]CAS解释:CAS(compareandswap),比较并交换。可以解决多线程并行情况下使用锁造成性能损耗的一种机制.CAS操作包含三个操作数—内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。一个线程从主内存中得到num值,并对num进行操作,写入值的时候,线程会把第一次取到的num值和主内存中num值进行比较,如果相等,就会将改变后的num写入主内存,如果不相等,则一直循环对比,知道成功为止。CAS

    2022年7月9日
    27
  • 数学的本质

    数学的本质数学的本质李国伟现代数学在方法上的特征现代数学在方法上最明显的特色是它的演绎性,就是由基本定义与公理出发,经逻辑推论到所有定理的发展方式。采取这种方法并非偶然,而是有内在的需求。我们要把一套概念讲清楚,必须用比较简单的概念来解释,但是这些概念又需要再加澄清,如此继续下去,如果不曾周而复始得到一个什么也说不清的恶性循环,便会无限延伸下去,达到一个不可知的前端。人类寻求知识的目的在组织自己

    2022年6月16日
    47
  • 下载verycd的方法下载电驴资源隐藏资源的最新可用方法

    下载verycd的方法下载电驴资源隐藏资源的最新可用方法我也是刚听说,现在电驴也不让下载了,和以前的狗狗一样,资源都屏蔽了,今天无意得到了一个可以下载电驴上的资源的方法,很简单,应该是漏洞,不知道能用多久,但是目前至少可以用。自2012年8月30日之后,verycd上所有资源的ed2k下载链接均被隐藏。没有登录的会员会显示“该资源为版权方声明保护内容,VeryCD不提供其下载”的字样。仅高等级的用户才能正常浏览到资源的e

    2022年8月10日
    6
  • Java和C语言有什么区别?[通俗易懂]

    Java和C语言有什么区别?[通俗易懂]Java和C语言作为现在行业中经常被人提起的两种语言,有很大的区别。选择不同的语言学习以后的发展也会大不相同,那么Java和C语言有什么区别呢?现在学哪种语言更合适呢?从概念上看,C语言是一门面向过程、抽象化的通用程序设计语言;Java是一门面向对象编程语言,而Java语言是从C语言衍生而来,它吸收了C++语言的各种优点,并且摒弃了C++里难以理解的多继承、指针等概念。从概念可以看出C语言相当…

    2022年7月7日
    20

发表回复

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

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