300. 最长递增子序列——【Leetcode每日刷题】

news2024/11/28 18:35:56

300. 最长递增子序列

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

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

示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

示例 2:

输入:nums = [0,1,0,3,2,3]
输出:4

示例 3:

输入:nums = [7,7,7,7,7,7,7]
输出:1

提示:

  • 1 < = n u m s . l e n g t h < = 2500 1 <= nums.length <= 2500 1<=nums.length<=2500
  • − 1 0 4 < = n u m s [ i ] < = 1 0 4 -10^4 <= nums[i] <= 10^4 104<=nums[i]<=104

进阶:

  • 你能将算法的时间复杂度降低到 O(n log(n)) 吗?

思路:

法一:动态规划

本题动态规划的关键就是 dp[i] ,表示 最后一位是 nums[i] 的最长上升子序列的长度。(注意: nums[i]必须被选取!)

动规五部曲:

  1. dp[i]的定义

dp[i] 表示 i 之前包括 i 的最长上升子序列的长度。

  1. 状态转移方程

位置 i 的最长升序子序列等于 j 从 0 到 i-1 各个位置的最长升序子序列 + 1 的最大值。

所以:if (nums[j] < nums[i]) dp[i] = max(dp[i], dp[j] + 1);

注意这里不是要 dp[i] 与 dp[j] + 1进行比较,而是我们要取 dp[j] + 1的最大值。

  1. dp[i]的初始化

每一个 i,对应的 dp[i](即最长上升子序列)起始大小至少都是是1.

  1. 确定遍历顺序

dp[i] 是有 0 到 i-1各个位置的最长升序子序列 推导而来,那么遍历 i 一定是从前向后遍历。j 其实就是0到 i-1,遍历i的循环里外层,遍历 j 则在内层。

  1. 举例推导dp数组

法二:贪心 + 二分查找

考虑一个简单的贪心,如果我们要使上升子序列尽可能的长,则我们需要让序列上升得尽可能慢,因此我们希望每次在上升子序列最后加上的那个数尽可能的小。

新建数组 tails,用于保存最长上升子序列。

对原序列进行遍历,将每位元素二分插入 tails 中。

  • 如果 tails 中元素都比它小,将它插到最后
  • 否则,用它覆盖掉比它大的元素中最小的那个。

总之,思想就是让 tails 中存储比较小的元素。这样,tails 未必是真实的最长上升子序列,但长度是对的。

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

反证法证明: 当 k<i ,若 tails[k] >= tails[i] ,代表较短子序列的尾部元素的值 > 较长子序列的尾部元素的值。这是不可能的,因为从长度为 i 的子序列尾部倒序删除 i−1 个元素,剩下的为长度为 k 的子序列,设此序列尾部元素值为 v,则一定有 v < tails[i](即长度为 k 的子序列尾部元素值一定更小), 这和 tails[k] >= tails[i] 矛盾。

既然严格递增,每轮计算 tails[k]时就可以使用二分法查找需要更新的尾部元素值的对应索引 i。

举个栗子:
例如:[1 2 5 2 3 5 3 4 5]

  1. 以1结尾的最长上升子序列是[1]很显然
  2. 接下来看以2结尾的最长上升子序列,显然2前面的最长上升且小于2的子序列,接上2是最长的,所以以2结尾的最长上升子序列是[1 2]
  3. 再看5,是[1 2 5]
  4. 以第二个2结尾的最长上升子序列是[1 2’ 5]
  5. 以3结尾的显然是[1 2’ 3]所以用3把5覆盖(这一步是贪心,因为反正都是长度为"3"的前缀,尾巴越小,后面越容易接)
  6. 再看下一个5,就变成[1 2’ 3 5’]
  7. 再下一个3’把前面的3顶替了,[1 2’ 3’ 5’]
  8. 再下一个4,接到3后面,因为贪心,把5顶替 [1 2’ 3’ 4]
  9. 最后一个5’‘接上去,这个桶就看起来是:[1 2’ 3’ 4 5’']

所以最长上升子序列长度为5,而且我们知道是第一个1,第二个2,第二个3,第一个4,第三个5,所以也可以找到序列位置

代码:(Java)

法一:动态规划

public class LengthOfLIS {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int [] nums = {10,9,2,5,3,7,101,18};
		System.out.println(lengthOfLIS(nums));
	}
	public static int lengthOfLIS(int[] nums) {
	 int n = nums.length;
	 
	 int[] dp = new int[n];
	 int len = 1;
	 for(int i = 0; i < n; i++) {
		 dp[i] = 1;
		 for(int j = 0; j < i; j++) {
			 if(nums[j] < nums[i]) {
				 dp[i] = Math.max(dp[i], dp[j] + 1);
			 }
		 }
		 len = Math.max(len, dp[i]);
	 }
	 return len;
 }
}

法二:贪心 + 二分查找

public class LengthOfLIS {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int [] nums = {10,9,2,5,3,7,101,18};
		System.out.println(lengthOfLIS(nums));
		
	}
	
	public static int lengthOfLIS(int[] nums) {
		int n = nums.length;
		
		int len = 0;
		
		for(int i = 0; i < n; i++) {
			int index = binarySearch(nums, len, nums[i]);
			nums[index] = nums[i];
			if(index == len) {
				len++;
			}
		}
		return len;
	}

	public static int binarySearch(int[] nums, int len, int num) {
		// TODO Auto-generated method stub
		int l = 0, h = len;
		while(l < h) {
			int mid = l + (h - l) / 2;
			if(nums[mid] == num) {
				return mid;
			}else if(nums[mid] > num) {
				h = mid;
			}else {
				l = mid + 1;
			}
		}
		return l;
	}
}

运行结果:

在这里插入图片描述

复杂度分析:

法一:

  • 时间复杂度 O ( n 2 ) O(n^2) O(n2),其中 n 为数组 nums 的长度。动态规划的状态数为 n,计算状态 dp[i] 时,需要 O(n) 的时间遍历 dp[0…i−1] 的所有状态,所以总时间复杂度为 O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度:O(n),需要额外使用长度为 n 的 dp 数组。

法二:

  • 时间复杂度:O(nlog⁡n)。数组 nums 的长度为 n,我们依次用数组中的元素去更新 当前最长子序列数组,而更新数组时需要进行 O(log⁡n) 的二分搜索,所以总时间复杂度为 O(nlog⁡n)。

  • 空间复杂度:O(1),在原数组上记录,不需要额外空间。

注:仅供学习参考!

题目来源:力扣。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/402187.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

leetcode 1648. 销售价值减少的颜色球

1648. 销售价值减少的颜色球这道题不知为何总想记录下来&#xff0c;思路很简单&#xff0c;但是实现总是出错&#xff0c;这也许就是要记录的原因。再一个觉得题解写的比较难以理解&#xff0c;所以再细致一些解析。希望可以帮到实在搞不懂的同学 思路: 目的&#xff1a;我们…

聚观早报|谷歌发布最大视觉语言模型;王兴投资王慧文ChatGPT项目

今日要闻&#xff1a;谷歌发布全球最大视觉语言模型&#xff1b;马斯克预计Twitter下季度现金流转正&#xff1b;王兴投资王慧文ChatGPT项目&#xff1b;美国拟明年 11 月开展载人绕月飞行&#xff1b;慧与科技宣布收购Athonet谷歌发布全球最大视觉语言模型 近日&#xff0c;来…

RocketMQ重复消费的症状以及解决方案

RocketMQ重复消费的症状以及解决方案 生产消息时重复 症状 当一条消息已被成功发送到 消费者 并完成持久化&#xff0c;此时出现了网络闪断或者客户端宕机&#xff0c;导致服务端对客户端应答失败。 如果此时 生产者 意识到消息发送失败并尝试再次发送消息&#xff0c;消费者…

学习 Python 之 Pygame 开发魂斗罗(十一)

学习 Python 之 Pygame 开发魂斗罗&#xff08;十一&#xff09;继续编写魂斗罗1. 改写主类函数中的代码顺序2. 修改玩家初始化3. 显示玩家生命值4. 设置玩家碰到敌人死亡5. 设置敌人子弹击中玩家6. 修改updatePlayerPosition()函数逻辑继续编写魂斗罗 在上次的博客学习 Pytho…

【Flutter从入门到入坑】Flutter 知识体系

学习 Flutter 需要掌握哪些知识&#xff1f; 终端设备越来越碎片化&#xff0c;需要支持的操作系统越来越多&#xff0c;从研发效率和维护成本综合考虑&#xff0c;跨平台开发一定是未来大前端的趋势&#xff0c;我们应该拥抱变化。而 Flutter 提供了一套彻底的移动跨平台方案…

【AlgorithmTraining】06:STL容器使用与练习(上)

STL容器的使用与练习&#xff08;上&#xff09; OVERVIEWSTL容器的使用与练习&#xff08;上&#xff09;string类1.vector动态数组&#xff1a;&#xff08;1&#xff09;vector动态一维数组&#xff1a;&#xff08;2&#xff09;vector动态二维数组&#xff1a;2.deque双端…

VR全景旅游,成为数字文旅破局关键!

导语&#xff1a;VR全景技术是一种基于虚拟现实技术的全景图像显示技术&#xff0c;已经在各个领域得到广泛应用。在旅游业中&#xff0c;VR全景技术也越来越受到重视&#xff0c;并逐渐成为旅游业发展的重要趋势。本文将介绍VR全景技术在旅游业中的应用场景和价值&#xff0c;…

【嵌入式Linux内核驱动】02_字符设备驱动

字符设备驱动 〇、基本知识 设备驱动分类 &#xff08;按共性分类方便管理&#xff09; 1.字符设备驱动 字符设备指那些必须按字节流传输&#xff0c;以串行顺序依次进行访问的设备。它们是我们日常最常见的驱动了&#xff0c;像鼠标、键盘、打印机、触摸屏&#xff0c;还有…

MATLAB——信号的采样与恢复

**题目&#xff1a;**已知一个连续时间信号 其中&#xff1a;f01HZ&#xff0c;取最高有限带宽频率fm5f0。分别显示原连续时间信号波形和 3种情况下抽样信号的波形。并画出它们的幅频特性曲线&#xff0c;并对采样后的信号进行恢复。 step1.绘制出采样信号 这部分相对简单…

面试知识点梳理及相关面试题(十一)-- docker

1. Docker和虚拟机的区别 容器不需要捆绑一整套操作系统&#xff0c;它只需要满足软件运行的最小内核就行了。 传统虚拟机技术是虚拟出一整套硬件后&#xff0c;在其上运行一个完成操作系统&#xff0c;在该系统上再运行所需应用进程容器内的应用进程直接运行于宿主的内核&am…

TCP

TCP 流量控制 一般来说,我们希望数据传输的快一些,但如果对方把数据发送的过快,接收方就可能来不及接收,这就会造成数据的丢失 流量控制就是让发送方的发送速率不要太快,让接收方来得及接收 利用滑动窗口机制可以在TCP连接上实现对发送方的流量控制 TCP接收方利用自己的接收…

青岛OJ(QingdaoU/OnlineJudge)部署如何直连数据库批量修改

1.postgres数据库QingdaoU/OnlineJudge用的数据库是postgreSQL&#xff0c;一个关系型数据库。默认端口是5432&#xff0c;我们下载一个navcat 15以上的版本&#xff0c;用来连数据库。2.修改docker-compose.yml文件修改docker-compose.yml&#xff0c;手动添加一个端口&#x…

一三四——一六七

一三四、JavaScript——_DOM简介 MDNq前端参考文档&#xff1a;DOM 概述 - Web API 接口参考 | MDN (mozilla.org) 一三五、JavaScript——HelloWorld <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta h…

【JSON文件解析】JSON文件

文章目录概要&#xff1a;本期主要介绍Qt解析JSON数据格式文件的方式。一、JSON数据格式1.JSON类似于XML&#xff0c;在JSON文件中&#xff0c;有且只有一个根节点2.JSON有两种主流包含型构造字符&#xff1a;{对象}、[数组]3.JSON的值主要包括&#xff1a;对象、数组、数字、字…

你还在调戏AI,有的公司已经用ChatGPT开展业务了

近日&#xff0c;OpenAI 正式宣布开放 ChatGPT 和 Whisper 两个模型的 API&#xff0c;API 版本的ChatGPT 不仅功能更多、性能更强&#xff0c;而且还更便宜一一相当于目前 GPT-3 模型价格打一折!划重点OpenAl正式开放 ChatGPT 和 Whisper 模型的 API&#xff0c;目前 SnapChat…

运营商大数据的发展现状和趋势

互联网时代&#xff0c;流量困局始终是困扰企业的一大难题。信息杂&#xff0c;无效投入多&#xff0c;商业性营销色彩浓厚&#xff0c;都在企业和客户之间树立起一层层厚厚的障壁。选择优秀的营销手段&#xff0c;对于一个企业来说至关重要&#xff0c;反之&#xff0c;如若在…

[2.2.4]进程管理——FCFS、SJF、HRRN调度算法

文章目录第二章 进程管理FCFS、SJF、HRRN调度算法&#xff08;一&#xff09;先来先服务&#xff08;FCFS, First Come First Serve&#xff09;&#xff08;二&#xff09;短作业优先&#xff08;SJF, Shortest Job First&#xff09;对FCFS和SJF两种算法的思考&#xff08;三…

Go语言之条件判断循环语句(if-else、switch-case、for、goto、break、continue)

一、if-else条件判断语句 Go中的if-else条件判断语句跟C差不多。但是需要注意的是&#xff0c;Go中强制规定&#xff0c;关键字if和else之后的左边的花括号"{“必须和关键字在同一行&#xff0c;若使用了else if结构&#xff0c;则前段代码快的右花括号”}"必须和关…

Navicat连接centos7 mysql失败解决思路

Navicat连接centos7 mysql失败&#xff0c;可以从一下的几个问题进行逐个排查。1、远程登录权限查看远程登录权限root用户的host值为localhost时&#xff0c;说明只能进行本地登录&#xff0c;需要将host改为“%”&#xff1b;UPDATE mysql.user SET host % WHERE user root;…

Java -数据结构,Map Set

一、搜索 1.1、概念及场景 Map和set是一种专门用来进行搜索的容器或者数据结构&#xff0c;其搜索的效率与其具体的实例化子类有关。以前常见的 搜索方式有&#xff1a; 直接遍历&#xff0c;时间复杂度为O(N)&#xff0c;元素如果比较多效率会非常慢二分查找&#xff0c;时间…