给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

题解

解法一:动态规划

解题思路:

  • 状态定义: dp[i] 的值代表 nums 以 nums[i] 结尾的最长子序列长度。
  • 转移方程: 设 j∈[0,i),考虑每轮计算新 dp[i] 时,遍历 [0,i) 列表区间,做以下判断:
    1.当 nums[i]>nums[j] 时: nums[i] 可以接在 nums[j] 之后(此题要求严格递增),此情况下最长上升子序列长度为 dp[j]+1 ;
    2.当 nums[i]<=nums[j] 时: nums[i] 无法接在 nums[j] 之后,此情况上升子序列不成立,跳过。

上述所有 1. 情况 下计算出的 dp[j]+1 的最大值,为直到 i 的最长上升子序列长度(即 dp[i] )。实现方式为遍历 j 时,每轮执行 dp[i]=max(dp[i],dp[j]+1)。
转移方程: dp[i] = max(dp[i], dp[j] + 1) for j in [0, i)。

初始状态: dp[i] 所有元素置 1,含义是每个元素都至少可以单独成为子序列,此时长度都为 1。
返回值: dp 列表最大值,即可得到全局最长上升子序列长度。

class Solution {public int lengthOfLIS(int[] nums) {//dp[i]代表以nums[i]结尾的LIS的长度int[] dp = new int[nums.length];//dp[i]最小值为1,因为以nums[i]结尾的LIS至少包含其自身Arrays.fill(dp,1);int res=0;//获得dp[i]数组for(int i=0;i<dp.length;i++){for(int j=i;j<dp.length;j++){//如果nums[j]>nums[i],代表nums[j]可以接在以nums[i]结尾的LIS后if(nums[j]>nums[i]){//找到nums[j]结尾的最长的递增子序列dp[j]=Math.max(dp[j],dp[i]+1);}}res=Math.max(res,dp[i]);}return res;}
}

解法二:动态规划 + 二分查找

解题思路:
降低复杂度切入点: 解法一中,遍历计算 dp 列表需 O(N),计算每个 dp[k] 需 O(N)。
1.动态规划中,通过线性遍历来计算 dp 的复杂度无法降低;
2.每轮计算中,需要通过线性遍历 [0,k) 区间元素来得到 dp[k] 。我们考虑:是否可以通过重新设计状态定义,使整个 dp 为一个排序列表;这样在计算每个 dp[k] 时,就可以通过二分法遍历 [0,k) 区间元素,将此部分复杂度由 O(N) 降至 O(logN)。

设计思路:
新的状态定义:
我们考虑维护一个列表 tails,其中每个元素 tails[k] 的值代表 长度为 k+1 的子序列尾部元素的值。
如 [1,4,6] 序列,长度为 1,2,3 的子序列尾部元素值分别为 tails=[1,4,6]。

状态转移设计:

  • 设常量数字 N,和随机数字 x,我们可以容易推出:当 N 越小时,N<x 的几率越大。例如: N=0 肯定比 N=1000 更可能满足 N<x。
  • 在遍历计算每个 tails[k],不断更新长度为 [1,k] 的子序列尾部元素值,始终保持每个尾部元素值最小 (例如 [1,5,3]], 遍历到元素 5 时,长度为 2 的子序列尾部元素值为 5;当遍历到元素 3 时,尾部元素值应更新至 3,因为 3 遇到比它大的数字的几率更大)。

tails 列表一定是严格递增的: 即当尽可能使每个子序列尾部元素值最小的前提下,子序列越长,其序列尾部元素值一定更大。

算法流程:

状态定义: tails[k] 的值代表 长度为 k+1 子序列 的尾部元素值。
转移方程: 设 res 为 tails 当前长度,代表直到当前的最长上升子序列长度。设 j∈[0,res),考虑每轮遍历 nums[k] 时,通过二分法遍历 [0,res) 列表区间,找出
nums[k] 的大小分界点
,会出现两种情况:

  • 区间中存在 tails[i]>nums[k] : 将第一个满足 tails[i]>nums[k] 执行 tails[i]=nums[k] ;因为更小的 nums[k] 后更可能接一个比它大的数字(前面分析过)。
  • 区间中不存在 tails[i]>nums[k] : 意味着 nums[k] 可以接在前面所有长度的子序列之后,因此肯定是接到最长的后面(长度为 res ),新子序列长度为 res+1。

初始状态:
令 tails 列表所有值 =0。
返回值:
返回 res ,即最长上升子子序列长度。

class Solution {public int lengthOfLIS(int[] nums) {int[] tails = new int[nums.length];//设 res 为 tails 当前长度,代表直到当前的最长上升子序列长度int res = 0;for(int num : nums){//i:指向tails的指针   j:初始指向res位置int i=0,j=res;while(i<j){//用于二分查找int m = (i+j)/2;//找到第一个num可以插入的位置if(tails[m]<num) i=m+1;else j=m;}//找到num可以插入的位置,插入tails[i]=num;//表明此时num直接插入了数组最后,右边界+1if(res == j) res++;}return res;}
}

Leetcode 300-最长递增子序列相关推荐

  1. 111. Leetcode 300. 最长递增子序列 (动态规划-子序列问题)

    步骤一.确定状态: 确定dp数组及下标含义 dp是长度为len(nums)的数组,dp[i]表示以nums[i]结尾的最长子序列的长度, 这个定义中 nums[i] 必须被选取,且必须是这个子序列的最 ...

  2. Leetcode 300 最长递增子序列 (每日一题 20210803)

    给你一个整数数组 nums ,找到其中最长严格递增子序列的长度.子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序.例如,[3,6,2,7] 是数组 [0,3,1,6,2 ...

  3. [LeetCode] 300 最长递增子序列 及返回这一子序列

    在原题的基础上,维护一个pos数组,当更新dp时,保存当前节点 i 的前一个结点 j 的下标(因为dp[i]是需要dp[j]来推出的). 另外用max和lastPos分别保存最长递增子序列的最后一个( ...

  4. LeetCode 300最长递增子序列

    题目链接:力扣​​​​​​s 思路:动态归划 定义dp数组:设 dp[i] 是以nums[i]结尾的最大递增子序列的长度 状态转移方程:dp[i]=max{dp[j]}+1,当nums[i]>n ...

  5. 2022-4-24 Leetcode 300.最长递增子序列

    class Solution {public:int lengthOfLIS(vector<int>& nums) {int n = nums.size();if(n <= ...

  6. LeetCode高频题300. 最长递增子序列

    LeetCode高频题300. 最长递增子序列 提示:本题是系列LeetCode的150道高频题,你未来遇到的互联网大厂的笔试和面试考题,基本都是从这上面改编而来的题目 互联网大厂们在公司养了一大批A ...

  7. 【Leetcode】最长递增子序列问题及应用

    文章目录 最长递增子序列问题及应用 300. 最长递增子序列 面试题 17.08. 马戏团人塔 354. 俄罗斯套娃信封问题 面试题 08.13. 堆箱子 1691. 堆叠长方体的最大高度 406. ...

  8. LeetCode 673. 最长递增子序列的个数

    LeetCode 673. 最长递增子序列的个数 文章目录 LeetCode 673. 最长递增子序列的个数 题目描述 一.解题关键词 二.解题报告 1.思路分析 2.时间复杂度 3.代码示例 2.知 ...

  9. LeetCode 673. 最长递增子序列的个数(DP)

    1. 题目 给定一个未排序的整数数组,找到最长递增子序列的个数. 示例 1: 输入: [1,3,5,4,7] 输出: 2 解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, ...

  10. leetcode - 673. 最长递增子序列的个数

    给定一个未排序的整数数组,找到最长递增子序列的个数. 示例 1: 输入: [1,3,5,4,7] 输出: 2 解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, 5, 7] ...

最新文章

  1. 【AI】caffe使用步骤(三):编写求解文件solver.prototxt
  2. 谷歌把安全融入主机芯片
  3. python课后题答案第五章_Python语言程序设计(美-梁勇)第5章习题解答
  4. 漫画说算法--动态规划算法三(绝对通俗易懂,非常棒)
  5. jquery $(document).ready() 与window.onload的区别
  6. Android小数和整数相互转换
  7. pycharm和python在mac里安装_MAC安装python-opencv及在pycharm下的配置
  8. HTTP状态码:400\500 错误代码
  9. gateway sentinel 熔断 不起作用_Sentinel 1.8.0 年度版本发布,熔断降级重构升级
  10. oracle中的hint是什么,SQL优化过程中常见Oracle中HINT的30个用法
  11. 全新防火墙6.0 DHCP线路上网配置
  12. php_redis配置安装php_redis-5.1.1-7.4-nts-vc15-x64.zip
  13. python贝叶斯分析方法实例_python 贝叶斯分析对应的代码
  14. Linux教程(第5版)习题部分习题及答案
  15. 单击屏幕亮屏流程分析
  16. 什么样的台灯灯光是好的?推荐中性色温的护眼台灯
  17. HashMap 扩容 加载因子
  18. 模拟信号拉线位移编码器是如何来校准的?
  19. 主动降噪耳机榜单,降噪耳机南卡和万魔哪个降噪好?
  20. 数据库中的视图理解和优点介绍

热门文章

  1. Revit 二次开发 获取多段轴网的location
  2. php操作 JSON格式数据
  3. 君子当如是——观《孔子》有感
  4. asp.Net Core 多线程之Task轻松学,快速上手
  5. oracle中sql查询
  6. 跑步时你最关心配速吗,真正会跑步的人更看重这个
  7. 服务器 稳定 重要性,云服务器 重要性
  8. 软件体系结构-03-层次体系结构概述
  9. flashback的配置
  10. 小心做好个人隐私保护!别让笔记本电脑成为隐私泄露的“间谍”