力扣中等难度热题——长度为K的子数组的能量值
目录
题目链接:3255. 长度为 K 的子数组的能量值 II - 力扣(LeetCode)
题目描述
示例
提示:
解法一:通过连续上升的长度判断
Java写法:
C++写法:
相比与Java写法的差别
运行时间
时间复杂度和空间复杂度
时间复杂度:
空间复杂度:
解法二:双指针+极限优化
优化前
Java写法:
优化前运行时间
优化后
优化的思路与实现:
Java写法:
C++写法:
优化分析:
运行时间
时间复杂度和空间复杂度
时间复杂度:
空间复杂度:
总结
解法一:通过连续上升的长度判断
解法二:双指针 + 极限优化
对比:
题目链接:3255. 长度为 K 的子数组的能量值 II - 力扣(LeetCode)
注:下述题目描述和示例均来自力扣
题目描述
给你一个长度为 n
的整数数组 nums
和一个正整数 k
。
一个数组的 能量值 定义为:
- 如果 所有 元素都是依次 连续 且 上升 的,那么能量值为 最大 的元素。
- 否则为 -1 。
你需要求出 nums
中所有长度为 k
的 子数组的能量值。
请你返回一个长度为 n - k + 1
的整数数组 results
,其中 results[i]
是子数组 nums[i..(i + k - 1)]
的能量值。
示例
示例 1:
输入:nums = [1,2,3,4,3,2,5], k = 3
输出:[3,4,-1,-1,-1]
解释:
nums
中总共有 5 个长度为 3 的子数组:
[1, 2, 3]
中最大元素为 3 。[2, 3, 4]
中最大元素为 4 。[3, 4, 3]
中元素 不是 连续的。[4, 3, 2]
中元素 不是 上升的。[3, 2, 5]
中元素 不是 连续的。
示例 2:
输入:nums = [2,2,2,2,2], k = 4
输出:[-1,-1]
示例 3:
输入:nums = [3,2,3,2,3,2], k = 2
输出:[-1,3,-1,3,-1]
提示:
1 <= n == nums.length <=
1 <= nums[i] <=
1 <= k <= n
解法一:通过连续上升的长度判断
-
初始化:
ans
数组用于存储每个长度为k
的子数组的能量值,初始时设置所有值为-1
,因为默认情况下每个子数组的能量值是-1
。cnt
用来记录当前子数组中连续上升的元素的个数。
-
遍历数组:
- 对于每个位置
i
,我们检查nums[i]
是否是连续上升序列的一部分。如果是,cnt
增加 1;如果不是,cnt
重置为 1。 - 如果当前连续上升的元素数
cnt
达到k
,则说明当前位置的子数组nums[i-k+1...i]
满足连续上升条件,并且它的能量值是当前的最大值,即nums[i]
。 - 然后将该子数组的能量值保存在
ans[i - k + 1]
中。
- 对于每个位置
-
返回结果:
- 最后返回结果数组
ans
,它包含每个长度为k
的子数组的能量值。
- 最后返回结果数组
Java写法:
class Solution {public int[] resultsArray(int[] nums, int k) {// 获取数组长度int n = nums.length;// 初始化结果数组,默认值为 -1int[] res = new int[n - k + 1];// 初始化数组 res 所有元素为 -1Arrays.fill(res, -1); // upLen 用来记录当前连续上升序列的长度int upLen = 0;// 遍历数组for (int i = 0; i < n; i++) {// 判断当前位置 nums[i] 是否是连续上升序列的一部分// 如果是,upLen 增加 1;如果不是,upLen 重置为 1if(i == 0 || nums[i] - nums[i - 1] != 1){upLen = 1;}else{upLen++;}// 如果当前连续上升的元素个数达到 k,更新对应子数组的能量值if (upLen >= k) {// 将该子数组的能量值(即最大元素 nums[i])保存在 res 中res[i - k + 1] = nums[i];}}// 返回结果数组return res;}
}
C++写法:
#include <vector>
#include <algorithm> // for std::fillclass Solution {
public:std::vector<int> resultsArray(std::vector<int>& nums, int k) {// 获取数组长度int n = nums.size();// 初始化结果数组,默认值为 -1std::vector<int> res(n - k + 1, -1);// upLen 用来记录当前连续上升序列的长度int upLen = 0;// 遍历数组for (int i = 0; i < n; i++) {// 判断当前位置 nums[i] 是否是连续上升序列的一部分// 如果是,upLen 增加 1;如果不是,upLen 重置为 1if (i == 0 || nums[i] - nums[i - 1] != 1) {upLen = 1;} else {upLen++;}// 如果当前连续上升的元素个数达到 k,更新对应子数组的能量值if (upLen >= k) {// 将该子数组的能量值(即最大元素 nums[i])保存在 res 中res[i - k + 1] = nums[i];}}// 返回结果数组return res;}
};
相比与Java写法的差别
- vector:在 C++ 中,我们用
std::vector<int>
来代替 Java 中的数组,vector
是 C++ 中的动态数组容器。 - std::fill:在 Java 中,我们使用
Arrays.fill
来填充数组,C++ 中对应的操作是std::fill
,不过在这里我们直接在初始化vector
时提供了默认值-1
,因此不需要额外的std::fill
。 - 数组大小:在 C++ 中,通过
nums.size()
获取数组的大小,n - k + 1
是结果数组的大小,表示有多少个长度为k
的子数组。 - 逻辑保持一致:其余的逻辑完全保留 Java 中的思路,主要是遍历数组,判断每个子数组是否满足“递增”条件,并更新结果数组。
运行时间
时间复杂度和空间复杂度
时间复杂度:
-
遍历数组:
主要的操作是在遍历nums
数组。遍历nums
数组的时间复杂度是 O(n),其中n
是数组的长度。 -
更新结果数组:
更新res[i - k + 1]
的操作是常数时间操作 O(1)。每次更新时,我们只做简单的赋值操作。
因此,总的时间复杂度是 O(n),其中 n
是输入数组 nums
的长度。
空间复杂度:
-
结果数组:
需要一个大小为n - k + 1
的数组res
来存储最终结果。该数组的空间复杂度是 O(n - k + 1),即 O(n)(因为n - k + 1
的量级与n
相同,忽略常数项)。 -
其他变量:
除了结果数组,还使用了几个常数空间的变量(如upLen
和i
)。这些是常数空间 O(1)。
因此,总的空间复杂度是 O(n),因为主要的空间消耗来自结果数组 res
。
![]() | ![]() |
解法二:双指针+极限优化
优化前
-
双指针定义:
left
指针指向当前窗口的左边界,right
指针指向当前窗口的右边界。- 初始时,
left = 0
,right = k - 1
,表示窗口包含了从nums[0]
到nums[k-1]
的元素。
-
滑动窗口:
- 每次通过右指针
right
来扩展窗口,在窗口内用指针p
来判断该子数组是否是一个连续的上升序列。 - 如果是连续的,结果数组
res[left]
记录下该子数组的最大值(即nums[right]
)。 - 如果不是连续的,
res[left]
标记为-1
。
- 每次通过右指针
-
窗口后移:
- 每次判断完一个窗口后,左指针
left
和右指针right
都向右移动一位,继续判断下一个子数组。
- 每次判断完一个窗口后,左指针
Java写法:
class Solution {public int[] resultsArray(int[] nums, int k) {// k是大于1小于n的选手,我们直接诶采用双指针int left = 0;int right = k - 1;int n = nums.length;int[] res = new int[n - k + 1];// 1,2,3,4,3,2,5// l rwhile(right < n){int p = right;// 使用p指针判断区间是否为连续的boolean flag = true;while(flag && p > left){// 如果不是连续的直接结束并标记flagif(nums[p] - nums[p - 1] != 1){flag = false;break;}// 没事就继续往下判断p--;}if(flag){// 证明是连续的,放入最大值res[left] = nums[right];}else{// 否则标记为-1res[left] = -1;}// 窗口区间后移left++;right++;}return res;}
}
优化前运行时间
显然是没有通过的,那么我们就进入优化操作吧。
优化后
我采用了标记变量 isOK
和 oldRight
来控制和优化窗口的滑动逻辑。具体优化的地方主要在于减少大量不必要的判断和重复的计算。
优化的思路与实现:
-
isOK
标志位:- 你引入了
isOK
标志变量,用来判断当前的窗口是否满足是连续上升的状态。如果是连续的,就可以直接根据oldRight
(即上一个窗口的右端)来判断是否继续。如果连续,就可以跳过一些不必要的计算,减少了重复的检查。
- 你引入了
-
oldRight
的使用:oldRight
用来记录上一个窗口右边界的元素值。当当前窗口的右边界的元素与oldRight
连续时,直接跳过重复计算,直接赋值并移动窗口指针。
-
判断子数组是否是连续的:
- 当
nums[right] - nums[left] != k - 1
时,直接返回-1
,标记当前子数组不符合条件,减少了不必要的判断。
- 当
-
通过
flag
判断连续性:- 内部的
while(flag)
判断窗口内是否是连续上升的子数组,如果是连续的,就将最大值(即nums[right]
)保存到结果数组中。
- 内部的
Java写法:
class Solution {public int[] resultsArray(int[] nums, int k) {// k是大于1小于n的选手,我们直接诶采用双指针int left = 0;int right = k - 1;int n = nums.length;int[] res = new int[n - k + 1];boolean isOK = false;int oldRight = -1;// 1,2,3,4,3,2,5// l rwhile(right < n){if(isOK && nums[right] - oldRight == 1){res[left] = nums[right];// System.out.print("是我" + nums[right]);oldRight = nums[right];// 窗口区间后移left++;right++;continue;}oldRight = nums[right];int p = right;// 使用p指针判断区间是否为连续的boolean flag = true;if(nums[right] - nums[left] != k - 1){res[left] = -1;// 窗口区间后移left++;right++;isOK = false;continue;}while(flag && p > left){// 如果不是连续的直接结束并标记flagif(nums[p] - nums[p - 1] != 1){flag = false;break;}// 没事就继续往下判断p--;}if(flag){// 证明是连续的,放入最大值res[left] = nums[right];isOK = true;}else{isOK = false;// 否则标记为-1res[left] = -1;}// 窗口区间后移left++;right++;}return res;}
}
C++写法:
#include <vector>
using namespace std;class Solution {
public:vector<int> resultsArray(vector<int>& nums, int k) {int n = nums.size();vector<int> res(n - k + 1, -1); // 初始化结果数组,默认为-1int left = 0, right = k - 1;// 滑动窗口从左到右移动while (right < n) {int p = right;bool flag = true;// 判断当前窗口是否为连续的上升序列while (flag && p > left) {if (nums[p] - nums[p - 1] != 1) {flag = false;break;}p--;}if (flag) {// 如果是连续的,存储当前窗口的最大值,即 nums[right]res[left] = nums[right];} else {// 如果不是连续的,标记为-1res[left] = -1;}// 窗口后移left++;right++;}return res;}
};
优化分析:
-
减少重复计算:
- 通过
isOK
标志位和oldRight
变量,避免了对已满足条件的窗口的重复检查,提升了效率。
- 通过
-
减少无效窗口判断:
- 如果当前子数组不符合连续上升的条件(
nums[right] - nums[left] != k - 1
),直接标记为-1
,并跳过后续的连续性判断,减少了不必要的计算。
- 如果当前子数组不符合连续上升的条件(
-
提高效率:
- 通过引入
isOK
标志,避免了对窗口中已满足条件部分的重复计算,提高了整体的处理速度。
- 通过引入
运行时间
时间复杂度和空间复杂度
时间复杂度:
- 外层
while (right < n)
:这个循环会遍历所有可能的窗口,每次窗口后移 1,最多运行n - k + 1
次。 - 内层
while(flag && p > left)
:在最坏的情况下,内层循环最多会执行k - 1
次(即窗口的最大长度)。因此,内层循环时间复杂度为 O(k)。
最终的时间复杂度是 O(n * k),和之前的复杂度相似。
空间复杂度:
- 主要空间消耗来自结果数组
res
,其大小为n - k + 1
,所以空间复杂度为 O(n - k + 1),即 O(n)。
![]() | ![]() |
总结
那么我们就成功的解决连续上升子数组能量值问题的两种解法,并提供了 Java 和 C++ 代码实现。问题要求对于一个数组 nums
,找到每个长度为 k
的子数组是否是连续上升的,如果是,则记录该子数组的最大值,否则标记为 -1。
解法一:通过连续上升的长度判断
- 思路:遍历数组,用
upLen
记录当前连续上升的元素个数。当upLen
达到k
时,记录该子数组的最大值到结果数组。 - 优点:简单,时间复杂度 O(n),空间复杂度 O(n)。
- 实现:使用 Java 和 C++ 语言实现,逻辑相同,使用
std::vector
或数组来存储结果。
解法二:双指针 + 极限优化
- 思路:通过双指针
left
和right
滑动窗口来判断每个子数组是否是连续上升的。引入isOK
标志和oldRight
来优化判断,减少不必要的计算。 - 优化:通过提前判断连续性,避免重复计算,提升效率。若当前子数组不满足条件,直接标记为 -1,跳过后续计算。
- 时间复杂度:O(n * k),与解法一相似,但减少了重复判断。
- 空间复杂度:O(n),空间消耗主要来自结果数组。
对比:
- 解法一适合简单情况,容易实现,但效率较低。
- 解法二通过优化滑动窗口的判断,减少了不必要的计算,提升了效率,适用于更大的输入数据。
相关文章:

力扣中等难度热题——长度为K的子数组的能量值
目录 题目链接:3255. 长度为 K 的子数组的能量值 II - 力扣(LeetCode) 题目描述 示例 提示: 解法一:通过连续上升的长度判断 Java写法: C写法: 相比与Java写法的差别 运行时间 时间复杂…...

JSON格式
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人和机器阅读和解析。它基于JavaScript的对象表示法,但被广泛用于多种编程语言。 JSON中的数据类型 字符串(String):用双引…...

O-RAN前传Spilt Option 7-2x
Spilt Option 7-2x 下行比特处理上行比特处理相关文章: Open Fronthaul wrt ORAN 联盟被称为下层拆分(LLS),其目标是提高电信市场的灵活性和竞争力。下层拆分是指无线电单元(RU) 和分布式单元(DU) 之间的拆分。 O-RAN前传接口可以在 eCPRI 上传输。eCPR…...

【GeoJSON在线编辑平台】(2)吸附+删除+挖孔+扩展
前言 在上一篇的基础上继续开发,补充上吸附功能、删除矢量、挖孔功能。 实现 1. 吸附 参考官方案例:Snap Interaction 2. 删除 通过 removeFeature 直接移除选中的要素。 3. 挖孔 首先是引入 Turf.js ,然后通过 mask 方法来实现挖孔的…...

确定图像的熵和各向异性 Halcon entropy_gray 解析
1、图像的熵 1.1 介绍 图像熵(image entropy)是图像“繁忙”程度的估计值,它表示为图像灰度级集合的比特平均数,单位比特/像素,也描述了图像信源的平均信息量。熵指的是体系的混乱程度,对于图像而言&#…...

大数据-214 数据挖掘 机器学习理论 - KMeans Python 实现 算法验证 sklearn n_clusters labels
点一下关注吧!!!非常感谢!!持续更新!!! 目前已经更新到了: Hadoop(已更完)HDFS(已更完)MapReduce(已更完&am…...

算法通关(3) -- kmp算法
KMP算法的原理 从题目引出 有两个字符串s1和s2,判断s1字符串是否包含s2字符串,如果包含返回s1包含s2的最左开头位置,不包含返回-1,如果是按照暴力的方法去匹配,以s1的每个字符作为开头,用s2的整体去匹配,…...

5G网卡network connection: disconnected
日志 5G流程中没有报任何错误,但是重新拿地址了,感觉像是驱动层连接断开了,dmesg中日志如下: [ 1526.558377] ippassthrough:set [ ip10.108.40.47 mask27 ip_net10.108.40.32 router10.108.40.33 dns221.12.1.227 221.12.33.227] br-lan […...

微积分复习笔记 Calculus Volume 1 - 4.9 Newton’s Method
4.9 Newton’s Method - Calculus Volume 1 | OpenStax...

Flutter自定义矩形进度条实现详解
在Flutter应用开发中,进度条是一个常见的UI组件,用于展示任务的完成进度。本文将详细介绍如何实现一个支持动画效果的自定义矩形进度条。 功能特点 支持圆角矩形外观平滑的动画过渡效果可自定义渐变色可配置边框宽度和颜色支持进度更新动画 实现原理 …...

如何设置 TORCH_CUDA_ARCH_LIST 环境变量以优化 PyTorch 性能
引言 在深度学习领域,PyTorch 是一个广泛使用的框架,它允许开发者高效地构建和训练模型。为了充分利用你的 GPU 硬件,正确设置 TORCH_CUDA_ARCH_LIST 环境变量至关重要。这个变量告诉 PyTorch 在构建过程中应该针对哪些 CUDA 架构版本进行优…...

CSS的三个重点
目录 1.盒模型 (Box Model)2.位置 (position)3.布局 (Layout)4.低代码中的这些概念 在学习CSS时,有三个概念需要重点理解,分别是盒模型、定位、布局 1.盒模型 (Box Model) 定义: CSS 盒模型是指每个 HTML 元素在页面上被视为一个矩形盒子。…...

【笔记】前后端互通中前端登录无响应
后来的前情提要 : 后端的ip地址在本地测试阶段应该设置为localhost 前端中写cors的配置 后端也要写cors的配置 且两者的url都要为localhost 前端写的baseUrl是指定对应的后端的ip地址以及端口号 很重要 在本地时后端的IP的地址也必须为本地的 F12的网页报错是&a…...

AI引领PPT创作:迈向“免费”时代的新篇章?
AI引领PPT创作:迈向“免费”时代的新篇章? 在信息爆炸的时代,演示文稿(PPT)作为传递信息和展示观点的重要工具,其制作效率和质量直接关系到演讲者的信息传递效果。随着人工智能(AI)…...

HTB:Perfection[WriteUP]
目录 连接至HTB服务器并启动靶机 1.What version of OpenSSH is running? 使用nmap对靶机TCP端口进行开放扫描 2.What programming language is the web application written in? 使用浏览器访问靶机80端口页面,并通过Wappalyzer查看页面脚本语言 3.Which e…...

鸿蒙next打包流程
目录 下载团结引擎 添加开源鸿蒙打包支持 打包报错 路径问题 安装DevEcoStudio 可以在DevEcoStudio进行打包hap和app 包结构 没法直接用previewer运行 真机运行和测试需要配置签名,DevEcoStudio可以自动配置, 模拟器安装hap提示报错 安装成功,但无法打开 团结1.3版本新增工具…...

uni-app 实现自定义底部导航
原博:https://juejin.cn/post/7365533404790341651 在开发微信小程序,通常会使用uniapp自带的tabBar实现底部图标和导航,但现实有少量应用使用uniapp自带的tabBar无法满足需求,这时需要自定义底部tabBar功能。 例如下图的需求&am…...

Vue前端开发:animate.css第三方动画库
在实际的项目开发中,如果自定义元素的动画,不仅效率低下,代码量大,而且还存在浏览器的兼容性问题,因此,可以借助一些优秀的第三动画库来协助完成动画的效果,如animate.css和gsap动画库ÿ…...

Java中的I/O模型——BIO、NIO、AIO
1. BIO(Blocking I/O) 1. 1 BIO(Blocking I/O)模型概述 BIO,即“阻塞I/O”(Blocking I/O),是一种同步阻塞的I/O模式。它的主要特点是,当程序发起I/O请求(比如…...

【软考知识】敏捷开发与统一建模过程(RUP)
敏捷开发模式 概述敏捷开发的主要特点包括:敏捷开发的常见实践包括:敏捷开发的优势:敏捷开发的挑战:敏捷开发的方法论: ScrumScrum 的核心概念Scrum 的执行过程Scrum 的适用场景 极限编程(XP)核…...

Redis常见面试题(二)
Redis性能优化 Redis性能测试 阿里Redis性能优化 使用批量操作减少网络传输 Redis命令执行步骤:1、发送命令;2、命令排队;3、命令执行;4、返回结果。其中 1 与 4 消耗时间 --> Round Trip Time(RTT,…...

业务模块部署
一、部署前端 1.1 window部署 下载业务模块前端包。 (此包为耐威迪公司发布,请联系耐威迪客服或售后获得) 包名为:业务-xxxx-business (注:xxxx为发布版本号) 此文件部署位置为:……...

【LeetCode】【算法】48. 旋转图像
LeetCode 48. 旋转图像 题目描述 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 思路 思路:再次拜见K神…...

【STM32F1】——9轴姿态模块JY901与串口通信(上)
【STM32F1】——9轴姿态模块JY901与串口通信(上) 一、简介 本篇主要对调试JY901模块的过程进行总结,实现了以下功能。 串口普通收发:使用STM32F103C8T6的USART2实现9轴姿态模块JY901串口数据的读取,并利用USART1发送到串口助手。 串口DMA收发:使用STM32F103C8T6的USART…...

Docker网络概述
1. Docker 网络概述 1.1 网络组件 Docker网络的核心组件包括网络驱动程序、网络、容器以及IP地址管理(IPAM)。这些组件共同工作,为容器提供网络连接和通信能力。 网络驱动程序:Docker支持多种网络驱动程序,每种驱动程…...

Vite与Vue Cli的区别与详解
它们的功能非常相似,都是提供基本项目脚手架和开发服务器的构建工具。 主要区别 Vite在开发环境下基于浏览器原生ES6 Modules提供功能支持,在生产环境下基于Rollup打包; Vue Cli不区分环境,都是基于Webpack。 在生产环境下&…...

深究JS底层原理
一、JS中八种数据类型判断方法 在JavaScript中,数据类型分为两大类:基本(原始)数据类型和引用(对象)数据类型。 基本数据类型(Primitive Data Types) 基本数据类型是表示简单的数…...

数据分析-41-时间序列预测之机器学习方法XGBoost
文章目录 1 时间序列1.1 时间序列特点1.1.1 原始信号1.1.2 趋势1.1.3 季节性和周期性1.1.4 噪声1.2 时间序列预测方法1.2.1 统计方法1.2.2 机器学习方法1.2.3 深度学习方法2 XGBoost2.1 模拟数据2.2 生成滞后特征2.3 切分训练集和测试集2.4 封装专用格式2.5 模型训练和预测3 参…...

json转java对象 1.文件读取为String 2.String转为JSONObject 3.JSONObject转为Class
一.参考王广帅的 服务器起服时的加载 private void readConfigFile(String configDir, Class<?> clazz) throws Exception {String fileName getConfigFileName(clazz);File configFile new File(configDir, fileName);// 读取所有的行,因此,应…...

基于卷积神经网络的农作物病虫害识别系统(pytorch框架,python源码)
更多图像分类、图像识别、目标检测等项目可从主页查看 功能演示: 基于卷积神经网络的农作物病虫害检测(pytorch框架)_哔哩哔哩_bilibili (一)简介 基于卷积神经网络的农作物病虫害识别系统是在pytorch框架下实现的…...