想要精通算法和SQL的成长之路 - 最长递增子序列 II(线段树的运用)
想要精通算法和SQL的成长之路 - 最长递增子序列 II(线段树的运用)
- 前言
- 一. 最长递增子序列 II
- 1.1 向下递推
- 1.2 向上递推
- 1.3 更新操作
- 1.4 查询操作
- 1.5 完整代码:
前言
想要精通算法和SQL的成长之路 - 系列导航
一. 最长递增子序列 II
原题链接
在做这个题目之前,先看一下:数据结构 - 线段树的运用 。
在线段树的基础上,思路如下:
- 首先,题目要求了子序列中,相邻的元素差不能超过
k
值。我们假设线段树的val
值,存储的就是最长递增子序列的长度。 - 我们定义
query
函数的返回就是范围区间内的最长递增子序列长度。
那么伪代码就是:
public int lengthOfLIS(int[] nums, int k) {int ans = 0;for (int i = 0; i < nums.length; i++) {int tmp = query(nums[i]);ans = Math.max(ans, tmp);}return ans;
}
但是有一个问题:假设我们以num[i]
作为最后一个元素,但是我并不知道它的前一个元素是谁。那咋办?
结合线段树的一个区间求值性质,我们只要求得区间 [num[i] - k, num[i] - 1]
之间的最长子序列长度,再加上1(当前子序列的最后一个元素num[i]
),那么就可以求得以num[i]
为结尾的最长子序列长度了。
同时我们还要更新各个子区间对应的最长长度,即伪代码:
for (int i = 0; i < nums.length; i++) {int tmp = query(nums[i]);update(tmp)ans = Math.max(ans, tmp);
}
1.1 向下递推
我们做更新操作的时候,求得不再是 数据结构 - 线段树的运用 里面的区间和,而是最大值。因此我们不能在原本值的基础上做加减法运算。而是做覆盖运算。
class Node {Node left, right;int val, add;
}private void pushDown(Node node) {if (node.left == null) {node.left = new Node();}if (node.right == null) {node.right = new Node();}if (node.add == 0) {return;}node.left.val = node.add; // 替换node.right.val = node.add; // 替换node.left.add = node.add; // 替换node.right.add = node.add; // 替换node.add = 0;
}
1.2 向上递推
求以当前节点作为最长子序列的最后一个元素时的序列长度时,我们可以拿到:
- 左子序列的最长递增长度。
- 右子序列的最长递增长度。
两者取最大,那么代码就是:
private void pushUp(Node node) {node.val = Math.max(node.left.val, node.right.val);
}
1.3 更新操作
public void update(Node node, int start, int end, int left, int right, int val) {// 如果线段树的区间完全在查询区间内,那么直接更新当前节点的 val 值即可if (start >= left && end <= right) {// 覆盖旧值node.val = val;// 覆盖需要传递的节点值node.add = val;return;}// 如果不在查询区间内,那么我们需要递归更新左右子树int mid = (start + end) >> 1;// 向下传递标记pushDown(node);if (left <= mid) {update(node.left, start, mid, left, right, val);}// [mid + 1, end] 和 [l, r] 可能有交集,遍历右孩子区间if (right > mid) {update(node.right, mid + 1, end, left, right, val);}// 计算当前节点的val值pushUp(node);
}
1.4 查询操作
public int query(Node node, int start, int end, int left, int right) {// 若当前区间完全在查询区间内,直接返回当前区间的最值if (left <= start && end <= right) {return node.val;}// 把当前区间 [start, end] 均分得到左右孩子的区间范围int mid = (start + end) >> 1, ans = 0;// 下推标记pushDown(node);// [start, mid] 和 [l, r] 可能有交集,遍历左孩子区间if (left <= mid) {ans = query(node.left, start, mid, left, right);}// [mid + 1, end] 和 [l, r] 可能有交集,遍历右孩子区间if (right > mid) {ans = Math.max(ans, query(node.right, mid + 1, end, left, right));}return ans;
}
1.5 完整代码:
有个问题就是:我们在遍历数组的每个元素num[i]的时候,我们的线段树区间应该设置为多少?
因为我们是以每个元素的 [num[i] - k, num[i] - 1]
区间来做计算的,因此线段树的范围和num[i]
的范围有关系。
题目有个提示:
那么确定好了线段树的区间范围,我们可以编写代码如下:
class Solution {public int lengthOfLIS(int[] nums, int k) {int ans = 0;Node root = new Node();for (int i = 0; i < nums.length; i++) {// 查询区间 [nums[i] - k, nums[i] - 1] 区间范围内的,以每个元素为末尾元素时的最长递增子序列长度。int cnt = query(root, 0, N, Math.max(0, nums[i] - k), nums[i] - 1) + 1;// 更新,注意这里是覆盖更新,对应的模版中覆盖更新不需要累加,已在下方代码中标注update(root, 0, N, nums[i], nums[i], cnt);ans = Math.max(ans, cnt);}return ans;}class Node {Node left, right;int val, add;}private int N = (int) 1e5;private Node root = new Node();public void update(Node node, int start, int end, int left, int right, int val) {// 如果线段树的区间完全在查询区间内,那么直接更新当前节点的 val 值即可if (start >= left && end <= right) {// 覆盖旧值node.val = val;// 覆盖需要传递的节点值node.add = val;return;}// 如果不在查询区间内,那么我们需要递归更新左右子树int mid = (start + end) >> 1;// 向下传递标记pushDown(node);if (left <= mid) {update(node.left, start, mid, left, right, val);}// [mid + 1, end] 和 [l, r] 可能有交集,遍历右孩子区间if (right > mid) {update(node.right, mid + 1, end, left, right, val);}// 计算当前节点的val值pushUp(node);}public int query(Node node, int start, int end, int left, int right) {// 若当前区间完全在查询区间内,直接返回当前区间的最值if (left <= start && end <= right) {return node.val;}// 把当前区间 [start, end] 均分得到左右孩子的区间范围int mid = (start + end) >> 1, ans = 0;// 下推标记pushDown(node);// [start, mid] 和 [l, r] 可能有交集,遍历左孩子区间if (left <= mid) {ans = query(node.left, start, mid, left, right);}// [mid + 1, end] 和 [l, r] 可能有交集,遍历右孩子区间if (right > mid) {ans = Math.max(ans, query(node.right, mid + 1, end, left, right));}return ans;}private void pushUp(Node node) {node.val = Math.max(node.left.val, node.right.val);}private void pushDown(Node node) {if (node.left == null) {node.left = new Node();}if (node.right == null) {node.right = new Node();}if (node.add == 0) {return;}node.left.add = node.add; // 不需要累加node.right.add = node.add; // 不需要累加node.left.val = node.add; // 不需要累加node.right.val = node.add; // 不需要累加node.add = 0;}
}
相关文章:

想要精通算法和SQL的成长之路 - 最长递增子序列 II(线段树的运用)
想要精通算法和SQL的成长之路 - 最长递增子序列 II(线段树的运用) 前言一. 最长递增子序列 II1.1 向下递推1.2 向上递推1.3 更新操作1.4 查询操作1.5 完整代码: 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 最长递增子序列 II 原题链接…...

java用easyexcel按模版导出
首先在项目的resources下面建一个template包,之后在下面创建一个模版,模版格式如下: 名称为 financeReportBillStandardTemplateExcel.xlsx: {.fee}类型的属性值,是下面实体类的属性,要注意这里面的格式&a…...

Servlet执行流程生命周期方法介绍体系结构、Request和Response的功能详解
🐌个人主页: 🐌 叶落闲庭 💨我的专栏:💨 c语言 数据结构 javaEE 操作系统 Redis 石可破也,而不可夺坚;丹可磨也,而不可夺赤。 Servlet 一、 Servlet执行流程二、Servlet生…...

软件工程之总体设计
总体设计是软件工程中的一个重要阶段,它关注整个系统的结构和组织,旨在将系统需求转化为可执行的软件解决方案。总体设计决定了系统的架构、模块划分、功能组织以及数据流和控制流等关键方面。 可行性研究 具体方面:经济可行性、技术可行性…...

监控员工电脑文件拷贝记录:电脑怎么看员工复制文件的历史记录
在现代企业管理中,数据安全和保密是极其重要的一环。企业需要确保敏感信息不被泄露,以防止可能的法律纠纷和经济损失。为此,许多公司都采取了一些措施来监控员工的电脑使用行为。其中,监控文件拷贝记录是一种常见的方法。本文将详…...
vue中request.js中axios请求和(若依)文件通用下载方法封装
vue中request.js中axios请求和(若依)文件通用下载方法封装 1.request.js import axios from axios import { Message, Loading } from element-ui import { saveAs } from file-saver // 创建axios实例 const request axios.create({// 这里可以放一…...

【大数据存储与处理】1. hadoop单机伪分布安装和集群安装
0. 写在前面 0.1 软件版本 hadoop2.10.2 ubuntu20.04 openjdk-8-jdk 0.2 hadoop介绍 Hadoop是一个由Apache基金会所开发的分布式系统基础架构。用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力进行高速运算和存储。Hadoop实现了一个…...

linux通过time命令统计代码编译时间
首先编写一个编译脚本 build.sh 内容如下: 然后执行time sh build.sh 编译完成后输出三个时间 time sh xxx.sh # 会返回3个时间数据 (1) real:从进程 ls 开始执行到完成所耗费的 CPU 总时间。该时间包括 ls 进程执行时实际使用的 CPU 时间,…...
logback日志是怎么保证多线程输出日志线程安全的
logback中的单例模式 logback日志框架使用了单例设计模式来进行日志输出。在logback中,Logger类是一个关键的组件,它负责记录和输出日志消息。 Logger类使用了单例设计模式,确保在一个应用程序中只存在一个Logger实例。这样做的好处是可以确…...
2022年统计用区划代码表SQL 01
行政区划代码为国家公布的六位县级以上行政区划代码 行政区编码的用途: APP里做城市级联选择根据身份证前六位获取用户所在城市区县 370786 昌邑市 370800 济宁市 370811 任城区 370812 兖州区 百度高德等接口通常都会返回adcode字段 (行政区编码)根据 行政区编…...
EM@基本初等函数@幂和根式@指数函数
abstract 基本初等函数幂和根式指数函数 指数和幂 正整指数幂 a n a^{n} an a ⋯ a ⏟ n 个 \underbrace{a\cdots{a}}_{n个} n个 a⋯a, n ∈ N n\in\mathbb{N^{}} n∈N 其中 a n a^{n} an称为** a a a的 n n n次幂** a a a叫做幂的底数, n n n叫做幂的指数 正整指数…...

时序预测 | MATLAB实现NGO-GRU北方苍鹰算法优化门控循环单元时间序列预测
时序预测 | MATLAB实现NGO-GRU北方苍鹰算法优化门控循环单元时间序列预测 目录 时序预测 | MATLAB实现NGO-GRU北方苍鹰算法优化门控循环单元时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现NGO-GRU北方苍鹰算法优化门控循环单元时间序列预测&#…...

element 二次确认框,内容自定义处理
上代码: async inspectionTypeOff(row) {console.log(row.id);let taskArray await this.getTaskList(row.id); // 查询关联的任务console.log("taskArray", taskArray);let messageTip taskArray.length > 0? <div><p>确认禁用巡检项&…...

【软件设计师-中级——刷题记录4(纯干货)】
目录 进度管理工具Grantt图:程序语言基础:高级语言源程序模式: 每日一言:持续更新中... 个人昵称:lxw-pro 个人主页:欢迎关注 我的主页 个人感悟: “失败乃成功之母”,这是不变的道理…...
9.24 校招 实习 内推 面经
绿泡*泡: neituijunsir 交流裙 ,内推/实习/校招汇总表 1、自动驾驶一周资讯 - 小马智行在京开展“车内无人”出行服务商业化试点,余承东将升任车BU董事长 自动驾驶一周资讯 - 小马智行在京开展“车内无人”出行服务商业化试点࿰…...
第二章:25+ Python 数据操作教程(第二十五节用 PYTHON 和 R 制作祝福圣诞节)持续更新
这篇文章献给所有 Python 和 R 编程爱好者...通过以下程序在同行中炫耀您的知识。作为一名数据科学专业人士,您希望自己的愿望在圣诞节前夕变得特别。如果您观察代码,您还可以学到 1-2 个技巧,您可以在以后的日常任务中使用这些技巧。 方法 1:运行以下程序,看看我的意思 R…...

你是怎么理解自动化测试的?理解自动化测试的目的和本质
其实自动化测试很好理解,由两部分组成,“自动化”和“测试”,所以我们要理解自动化测试,就必须理解“自动化”和“测试”,只有理解了这些概念,才能更轻松的做好的自动化测试。其中“自动化”可以想象成通过…...

二十六、MySQL并发事务问题:脏读/不可重复读/幻读
1、事务的隔离级别 (1)隔离级别 Read uncommitted # 读,未提交 Read committed # 读,已提交 Repeatable Read(默认) # 可重复读 Serializable # 串读 (2)基础语法 set transaction isolation level 事…...

RK3588平台开发系列讲解(项目篇)视频监控之RTMP推流
文章目录 一、RTMP协议是什么二、RTMP 的原理三、Nginx 流媒体服务器四、FFmpeg 推流沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 目前常见的视频监控和视频直播都是使用了 RTMP、RTSP、HLS、MPEG-DASH、WebRTC流媒体传输协议等。 视频监控项目组成,分为三部分:…...

http基础教程(超详细)
HTTP HTTP 一 、基础概念 请求和响应报文URL 二、HTTP 方法 GETHEADPOSTPUTPATCHDELETEOPTIONSCONNECTTRACE 三、HTTP 状态码 1XX 信息2XX 成功3XX 重定向4XX 客户端错误5XX 服务器错误 四、HTTP 首部 通用首部字段请求首部字段响应首部字段实体首部字段 五、具体应用 连接管理…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...
LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)
在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...
数据库——redis
一、Redis 介绍 1. 概述 Redis(Remote Dictionary Server)是一个开源的、高性能的内存键值数据库系统,具有以下核心特点: 内存存储架构:数据主要存储在内存中,提供微秒级的读写响应 多数据结构支持&…...
JS红宝书笔记 - 3.3 变量
要定义变量,可以使用var操作符,后跟变量名 ES实现变量初始化,因此可以同时定义变量并设置它的值 使用var操作符定义的变量会成为包含它的函数的局部变量。 在函数内定义变量时省略var操作符,可以创建一个全局变量 如果需要定义…...
webpack面试题
面试题:webpack介绍和简单使用 一、webpack(模块化打包工具)1. webpack是把项目当作一个整体,通过给定的一个主文件,webpack将从这个主文件开始找到你项目当中的所有依赖文件,使用loaders来处理它们&#x…...
Django RBAC项目后端实战 - 03 DRF权限控制实现
项目背景 在上一篇文章中,我们完成了JWT认证系统的集成。本篇文章将实现基于Redis的RBAC权限控制系统,为系统提供细粒度的权限控制。 开发目标 实现基于Redis的权限缓存机制开发DRF权限控制类实现权限管理API配置权限白名单 前置配置 在开始开发权限…...

Appium下载安装配置保姆教程(图文详解)
目录 一、Appium软件介绍 1.特点 2.工作原理 3.应用场景 二、环境准备 安装 Node.js 安装 Appium 安装 JDK 安装 Android SDK 安装Python及依赖包 三、安装教程 1.Node.js安装 1.1.下载Node 1.2.安装程序 1.3.配置npm仓储和缓存 1.4. 配置环境 1.5.测试Node.j…...