Leetcode 300-最长递增子序列
给你一个整数数组 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-最长递增子序列相关推荐
- 111. Leetcode 300. 最长递增子序列 (动态规划-子序列问题)
步骤一.确定状态: 确定dp数组及下标含义 dp是长度为len(nums)的数组,dp[i]表示以nums[i]结尾的最长子序列的长度, 这个定义中 nums[i] 必须被选取,且必须是这个子序列的最 ...
- Leetcode 300 最长递增子序列 (每日一题 20210803)
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度.子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序.例如,[3,6,2,7] 是数组 [0,3,1,6,2 ...
- [LeetCode] 300 最长递增子序列 及返回这一子序列
在原题的基础上,维护一个pos数组,当更新dp时,保存当前节点 i 的前一个结点 j 的下标(因为dp[i]是需要dp[j]来推出的). 另外用max和lastPos分别保存最长递增子序列的最后一个( ...
- LeetCode 300最长递增子序列
题目链接:力扣s 思路:动态归划 定义dp数组:设 dp[i] 是以nums[i]结尾的最大递增子序列的长度 状态转移方程:dp[i]=max{dp[j]}+1,当nums[i]>n ...
- 2022-4-24 Leetcode 300.最长递增子序列
class Solution {public:int lengthOfLIS(vector<int>& nums) {int n = nums.size();if(n <= ...
- LeetCode高频题300. 最长递增子序列
LeetCode高频题300. 最长递增子序列 提示:本题是系列LeetCode的150道高频题,你未来遇到的互联网大厂的笔试和面试考题,基本都是从这上面改编而来的题目 互联网大厂们在公司养了一大批A ...
- 【Leetcode】最长递增子序列问题及应用
文章目录 最长递增子序列问题及应用 300. 最长递增子序列 面试题 17.08. 马戏团人塔 354. 俄罗斯套娃信封问题 面试题 08.13. 堆箱子 1691. 堆叠长方体的最大高度 406. ...
- LeetCode 673. 最长递增子序列的个数
LeetCode 673. 最长递增子序列的个数 文章目录 LeetCode 673. 最长递增子序列的个数 题目描述 一.解题关键词 二.解题报告 1.思路分析 2.时间复杂度 3.代码示例 2.知 ...
- LeetCode 673. 最长递增子序列的个数(DP)
1. 题目 给定一个未排序的整数数组,找到最长递增子序列的个数. 示例 1: 输入: [1,3,5,4,7] 输出: 2 解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, ...
- leetcode - 673. 最长递增子序列的个数
给定一个未排序的整数数组,找到最长递增子序列的个数. 示例 1: 输入: [1,3,5,4,7] 输出: 2 解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, 5, 7] ...
最新文章
- 【AI】caffe使用步骤(三):编写求解文件solver.prototxt
- 谷歌把安全融入主机芯片
- python课后题答案第五章_Python语言程序设计(美-梁勇)第5章习题解答
- 漫画说算法--动态规划算法三(绝对通俗易懂,非常棒)
- jquery $(document).ready() 与window.onload的区别
- Android小数和整数相互转换
- pycharm和python在mac里安装_MAC安装python-opencv及在pycharm下的配置
- HTTP状态码:400\500 错误代码
- gateway sentinel 熔断 不起作用_Sentinel 1.8.0 年度版本发布,熔断降级重构升级
- oracle中的hint是什么,SQL优化过程中常见Oracle中HINT的30个用法
- 全新防火墙6.0 DHCP线路上网配置
- php_redis配置安装php_redis-5.1.1-7.4-nts-vc15-x64.zip
- python贝叶斯分析方法实例_python 贝叶斯分析对应的代码
- Linux教程(第5版)习题部分习题及答案
- 单击屏幕亮屏流程分析
- 什么样的台灯灯光是好的?推荐中性色温的护眼台灯
- HashMap 扩容 加载因子
- 模拟信号拉线位移编码器是如何来校准的?
- 主动降噪耳机榜单,降噪耳机南卡和万魔哪个降噪好?
- 数据库中的视图理解和优点介绍