算法通关村第十六关黄金挑战——求滑动窗口中的最大值(滑动窗口与堆方法、双端队列法和直接比较法)
大家好,我是怒码少年小码。
今天这篇就讲一道题目,不难😎,但是一定要学会自己思考。
滑动窗口最大值
LeetCode 239:给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值。
示例 1:
- 输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
- 输出:[3,3,5,5,6,7]
解释:滑动窗口的位置 __ 窗口内的最大值
- [1 3 -1] -3 5 3 6 7 _______
3
- 1 [3 -1 -3] 5 3 6 7 _______
3
- 1 3 [-1 -3 5] 3 6 7 _______
5
- 1 3 -1 [-3 5 3] 6 7 _______
5
- 1 3 -1 -3 [5 3 6] 7 _______
6
- 1 3 -1 -3 5 [3 6 7] _______
7
直接比较法
首先,我第一个想到的是滑动窗口+直接比较的方法,既然是求每次滑动窗口的最大值,那就维护两个指针,当两个指针每次移动的时候都求一下当前窗口内的最大值,求出后放到存放最大值的数组中。这样一直到右指针到达数组的末尾。
public int[] maxSlidingWindow(int[] nums, int k) {int left = 0;int right = k - 1;int len = nums.length- k + 1 ;int[] maxList = new int[len];while(right < nums.length){int max = nums[left];for(int i = left ; i <= right ; i++ ){if(nums[i] > max){max = nums[i];}}maxList[left] = max;left++;right++;}return maxList;
}
当然,很不幸,这种方法超出了时间限制😎🤏🕶 -> 😭。接下来讲的方法才是本篇的重点。
滑动窗口与堆
本题初始时,我们将数组 nums 的前 k个元素放入优先队列中。每当我们向右移动窗口时,我们就可以把一个新的元素放入优先队列中,此时堆顶的元素就是堆中所有元素的最大值。然而这个最大值可能并不在滑动窗口中,在这种情况下,这个值在数组 nums 中的位置出现在滑动窗口左边界的左侧。因此,当我们后续继续向右移动窗口时,这个值就永远不可能出现在滑动窗口中了,我们可以将其永久地从优先队列中移除。
我们不断地移除堆顶的元素,直到其确实出现在滑动窗口中。此时,堆顶元素就是滑动窗口中的最大值。为了方便判断堆顶元素与滑动窗口的位置关系,我们可以在优先队列中存储二元组 (num,index),表示元素num 在数组中的下标为index。
public int[] maxSlidingWindow(int[] nums, int k) {int n = nums.length;//定义优先级队列,自定义排序器,首先按照nums元素值进行降序排序,如果元素值相等,则按照数组下标值进行降序排序PriorityQueue<int[]> pq = new PriorityQueue<int[]>(new Comparator<int[]>(){public int compare(int[] pair1 , int[] pair2){return pair1[0] != pair2[0] ? pair2[0]-pair1[0]:pair2[1]-pair1[1];}});// 前k个元素入队for(int i =0;i < k ; i++){pq.offer(new int[]{nums[i],i});}// 初始化结果数组int[] ans = new int[n - k + 1];ans[0] = pq.peek()[0];// 开始滑动窗口for(int i = k ; i < n ; i++){// 新的元素入队pq.offer(new int[]{nums[i],i});// 因为已经排好序,因此可以通过peek剔除掉当前队列中为最大值但非窗口中的的元素,循环结束后则队首元素为当前队列中为最大值且是窗口中的元素while(pq.peek()[1] <= i - k){pq.poll();}ans[i - k + 1] = pq.peek()[0];}return ans;
}
首先,我们有一个整数数组 nums
和一个窗口大小 k
。我们需要找到每个窗口中的最大值,并将这些最大值存储在一个新的数组 ans
中。
代码的核心是使用优先队列(PriorityQueue)来维护窗口中的元素,并根据它们的值和索引进行比较。
首先,我们创建一个优先队列 pq
,并通过传入一个自定义的比较器来定义元素的比较规则。比较器中的比较规则是根据元素的值和索引进行比较,如果元素的值不相等,则按值的降序排列,如果元素的值相等,则按索引的降序排列。
接下来,我们遍历数组 nums
的前 k
个元素,并将它们添加到优先队列 pq
中。每个元素都是一个数组,包含元素的值和索引。
new int[]{nums[i], i}
是一个匿名整数数组对象的创建和初始化。它的作用是创建一个包含两个元素的整数数组,并将 nums[i] 赋值给数组的第一个元素,将 i 赋值给数组的第二个元素。
在这个特定的代码中,我们使用 new int[]{nums[i], i} 来创建一个包含当前元素值 nums[i] 和当前索引 i 的整数数组。然后,我们将这个数组添加到优先队列 pq 中,以便在后续的操作中使用。
然后,我们创建一个新的数组 ans
,用于存储每个窗口的最大值。我们首先将优先队列 pq
中的最大元素的值存储在 ans
的第一个位置。
接下来,我们从第 k
个元素开始遍历数组 nums
。对于每个元素,我们将其添加到优先队列 pq
中,并执行以下操作:
-
检查优先队列
pq
的顶部元素(最大元素)的索引是否在当前窗口范围内。如果不在范围内,说明该元素已经不在当前窗口中,我们需要将其从优先队列pq
中移除。我们反复执行此操作,直到顶部元素的索引在当前窗口范围内。 -
将优先队列
pq
的顶部元素的值存储在ans
数组中的相应位置。这个值就是当前窗口的最大值。
重复以上步骤,直到遍历完整个数组 nums
。
最后,我们返回数组 ans
,其中包含了每个窗口的最大值。
双端队列
这种方法就当是一个小扩展
第一种方法在每个窗口内通过遍历查找最大值,时间复杂度为 O(k)。可以使用双端队列(Deque) 来优化这个过程,将当前窗口内的较小元素从队列中移除,以保持队列的头部始终是窗口内的最大值的下标。这样可以将时间复杂度降低到 O(1)。
public int[] maxSlidingWindow(int[] nums, int k) {int n = nums.length;if (n * k == 0) return new int[0];Deque<Integer> deque = new ArrayDeque<>();int[] maxList = new int[n - k + 1];for (int i = 0; i < nums.length; i++) {// 移除超出窗口范围的元素if (!deque.isEmpty() && deque.peek() < i - k + 1) {deque.poll();}// 移除窗口内小于当前元素的元素,保持队列头部始终是最大值while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {deque.pollLast();}//队列中加入数组的下标deque.offer(i);// 将窗口内的最大值存储在结果数组中if (i - k + 1 >= 0) {maxList[i - k + 1] = nums[deque.peek()];}}return maxList;
}
小码补充:
防止有人不了解Java中的双端队列,这里我们做一个简单的知识补充
在Java中,Deque
接口是双端队列(Double Ended Queue)的一种实现。Deque
具有队列和栈的性质,可以在队列的两端进行插入和删除操作。下面逐一解释 Deque
接口中的四个方法:poll()
、peek()
、peekLast()
和 offer()
。
poll()
方法用于检索并删除队列的头元素(首部元素)。如果队列为空,poll()
方法将返回null
。peek()
方法用于检索队列的头元素(首部元素),但不删除它。如果队列为空,peek()
方法将返回null
。peekLast()
方法用于检索队列的尾元素(尾部元素),但不删除它。如果队列为空,peekLast()
方法将返回null
。offer()
方法用于在队列的尾部插入一个元素。如果队列已满,则offer()
方法将返回false
,否则返回true
。
下面是这些方法的示例用法:
import java.util.*;
public class DequeExample {public static void main(String[] args) {Deque<Integer> deque = new ArrayDeque<>();// 添加元素到队列尾部deque.offer(1);deque.offer(2);deque.offer(3);System.out.println(deque); // 输出: [1, 2, 3]// 检索并删除队列头部元素int first = deque.poll();System.out.println(first); // 输出: 1System.out.println(deque); // 输出: [2, 3]// 检索队列头部元素但不删除int peeked = deque.peek();System.out.println(peeked); // 输出: 2// 检索队列尾部元素但不删除int peekedLast = deque.peekLast();System.out.println(peekedLast); // 输出: 3}
}
总结:
Deque
接口中的poll()
、peek()
、peekLast()
和offer()
方法分别用于检索和操作双端队列的元素。poll()
方法从队列头部检索并删除元素,peek()
方法从队列头部检索元素但不删除,peekLast()
方法从队列尾部检索元素但不删除,offer()
方法将元素插入到队列尾部。
END
说实话,还是很有难度的,那个滑动窗口和堆的配合我也是想了半天才搞懂,不就是力扣上的难度题目,我没事😎🤏🕶 -> 😭 。
相关文章:

算法通关村第十六关黄金挑战——求滑动窗口中的最大值(滑动窗口与堆方法、双端队列法和直接比较法)
大家好,我是怒码少年小码。 今天这篇就讲一道题目,不难😎,但是一定要学会自己思考。 滑动窗口最大值 LeetCode 239:给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。…...

常见树种(贵州省):009楠木、樟木、桂木种类
摘要:本专栏树种介绍图片来源于PPBC中国植物图像库(下附网址),本文整理仅做交流学习使用,同时便于查找,如有侵权请联系删除。 图片网址:PPBC中国植物图像库——最大的植物分类图片库 一、楠木 …...

全志H616开发版
开发板介绍: 二、开发板刷机 SDFormatter TF卡的格式化工具、Win32Diskimager 刷机工具 刷机镜像为:Orangepizero2_2.2.0_ubuntu_bionic_desktop_linux4.9.170.img 使用MobaXterm_Personal_20.3连接使用 网络配置:nmcli dev wifi 命令接入网…...

【Spring boot】RedisTemplate中String、Hash、List设置过期时间
文章目录 前言Redis中String设置时间的方法Redis中Hash和List设置时间的方法Redis中Hash的put、putAll、putIfAbsent区别 前言 时间类型:TimeUnit import java.util.concurrent.TimeUnit;TimeUnit.SECONDS:秒 TimeUnit.MINUTES:分 TimeUnit.HOURS&…...

Nosql之redis概述及基本操作
关系数据库与非关系型数据库概述 关系型数据库 关系型数据库是一个结构化的数据库,创建在关系模型(二维表格模型)基础上,一般面向于记录。SQL语句(标准数据查询语言)就是一种基于关系型数据库的语言,用于执行对关系型…...

使ros1和ros2的bag一直互通
很多文章都是先source ros1 然后source ros2,再play bag source /opt/ros/noetic/setup.bash source /opt/ros/foxy/setup.bash ros2 bag play -s rosbag_v2 kitti_raw00.bag 但实测会出问题: 为使ros1和ros2的bag一直互通 sudo apt update sudo apt install ros-foxy-ro…...

【正点原子 linux 驱动编程】
在此声明,正用点编的说明书真的拉,丝毫不具备兼容性。。比如linux的第一个实验,其中包含的 unregister_chrdev_region 函数,fileoperation 结构体等均来自 <linux/fs.h> 文件,搞不懂,他们方ide.h&…...

使用Python的turtle模块绘制玫瑰花图案(含详细Python代码与注释)
1.1引言 turtle模块是Python的标准库之一,它提供了一个绘图板,让我们可以在屏幕上绘制各种图形。通过使用turtle,我们可以创建花朵、叶子、复杂的图案等等。本博客将介绍如何使用turtle模块实现绘制图形的过程,并展示最终结果。 …...

Redis学习笔记14:基于spring data redis及lua脚本ZSET有序集合实现环形结构案例及lua脚本如何发送到redis服务器
案例实现目标,一、实现一个环形结构,环形结构上节点有一个阀值threshold,超过阀值则移除分数score最低的成员,不足则将当前成员添加进环中,且确保成员不可重复;二、每次访问环中的数据都需要刷新key的过期时间…...

openssl C++研发之pem格式处理详解
一、PEM_writeXXX和EM_write_bio_XXX 在OpenSSL的crypto/pem.h头文件中,PEM_write_XXXX和PEM_write_bio_XXXX系列函数用于将特定类型的数据写入文件或BIO(内存缓冲区)中,其中XXXX代表不同的数据类型。 这些函数的使用方式相似&a…...

【教3妹学编辑-mysql】详解数据库三大范式
什么是范式 简单地理解就是:数据库设计时遵循的规范 三大范式 数据库三大范式包含:1、第一范式(1NF);2、第二范式(2NF);3、第三范式(3NF)。其中,第一范式(1NF)的要求是属性不可分割,第二范式(2NF)的要求是…...

【计算机网络笔记】路由算法之链路状态路由算法
系列文章目录 什么是计算机网络? 什么是网络协议? 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能(1)——速率、带宽、延迟 计算机网络性能(2)…...

读像火箭科学家一样思考笔记04_第一性原理(下)
1. 来自无形规则的阻力 1.1. 无形规则 1.1.1. 僵化成规则的不必要习惯和行为 1.1.2. 不像有形的书面规则 1.1.2.1. 书面规则出现在标准操作流程中,可以修改或删除 1.1.3. 成文的规则可能会抗拒变革,但无形规则却更加顽固 1.1.4. 我们为强加在自己身…...

开源集群管理系统对比分析:Kubernetes 与 Apache Mesos
集群管理系统是关键的软件解决方案,可以在互连机器网络中有效分配和利用计算资源。毫无疑问,它们通过确保可扩展性、高可用性和有效的资源管理在现代计算中发挥着至关重要的作用,这使得它们对于运行复杂的应用程序、管理数据中心以及进一步增…...

matlab 坡度滤波算法地面分割
目录 一、算法原理1、实现流程2、参考文献二、代码实现三、结果展示四、测试数据一、算法原理 1、实现流程 1、格网示意图 2、计算格网行列数 公式中的特殊符号为向上取整,...

【腾讯云 HAI域探秘】高性能服务器引领AI革新浪潮:从AI绘画、知识问答到PyTorch图像分类、视频检测的全方位探索
目录 1 HAI(高性能应用服务)简介2 HAI的应用场景2.1 HAI在AI作画中的灵活性与效率2.2 深入探索LLM语言模型的应用与性能2.3 HAI支持的AI模型开发环境与工具 3 基于stable difussio的AI 绘画应用实践3.1 使用AI模型中的stable diffusion模型服务3.2 设置和…...

【Java】ExcelWriter自适应宽度工具类(支持中文)
工具类 import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet;/*** Excel工具类** author xiaoming* date 2023/11/17 10:40*/ public class ExcelUti…...

C++二分查找算法:132模式枚举3简洁版
本文涉及的基础知识点 二分查找算法合集 本题不同解法 包括题目及代码C二分查找算法:132 模式解法一枚举3C二分查找算法:132 模式解法二枚举2代码简洁C二分查找算法:132 模式解法三枚举1性能最佳C单调向量算法:132 模式解法三枚…...

Map 和 WeakMap:JavaScript 中的键值对集合
JavaScript 是一种动态、弱类型的脚本语言,经常用于构建现代 Web 应用程序。在编写 JavaScript 代码时,我们经常需要使用各种数据结构来存储和管理数据。其中,Map 和 WeakMap 就是两个非常有用的数据结构,它们分别提供了用于存储键…...

linux rsyslog综合实战1
本次我们通过rsyslog服务将A节点服务器上的单个日志(Path:/var/log/245-1.log)实时同步到B节点服务器目录下(Path:/opt/rsyslog/245) 1.rsyslog架构 2.环境信息 环境信息 HostnameIpAddressOS versionModuleNotersyslog1192.168.10.245CentOS Linux release 7.9.2009 (Core)rs…...

redis+python 建立免费http-ip代理池;验证+留接口
前言: 效果图: 对于网络上的一些免费代理ip,http的有效性还是不错的;但是,https的可谓是凤毛菱角; 正巧,有一个web可以用http访问,于是我就想到不如直接拿着免费的HTTP代理去做这个! 思路: 1.单页获取ipporttime (获取time主要是为了后面使用的时候,依照时效可以做文章) 2.整…...

虚幻C++ day5
角色状态的常见机制 创建角色状态设置到UI上 在MainPlayer.h中新建血量,最大血量,耐力,最大耐力,金币变量,作为角色的状态 //主角状态UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category "Playe Stats&…...

C#中的DateTime类
C# 中的 DateTime 类是用于表示日期和时间的结构。它提供了一系列属性和方法,用于处理日期和时间的各种操作和计算。下面是一些常用的 DateTime 类的用法和方法解释,以及相应的示例说明: 创建 DateTime 对象: 使用当前日期和时间创…...

Flutter笔记:Matrix4矩阵变换与案例
Flutter笔记 Matrix4矩阵变换及其案例 作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263 邮箱 :291148484163.com 本文地址:https://blog.csdn.net/qq_28550263/article/details/134474764 【简介…...

数字IC前端学习笔记:时钟切换电路
相关阅读 数字IC前端https://blog.csdn.net/weixin_45791458/category_12173698.html?spm1001.2014.3001.5482 有些时候我们需要在系统运行时切换系统时钟,最简单的方法就是使用一个MUX(数据选择器)选择输出的时钟,如下代码片所…...

.NET6使用MiniExcel根据数据源横向导出头部标题及数据
.NET6MiniExcel根据数据源横向导出头部标题 MiniExcel简单、高效避免OOM的.NET处理Excel查、写、填充数据工具。 特点: 低内存耗用,避免OOM、频繁 Full GC 情况 支持即时操作每行数据 兼具搭配 LINQ 延迟查询特性,能办到低消耗、快速分页等复杂查询 轻量…...

表内容的操作(增删查改)【MySQL】
文章目录 表的 CRUDCreate(增加)插入记录插入冲突则更新记录替换记录 Retrieve(查找)查找记录指定表达式的别名为结果去重WHERE 子句运算符条件查询区间查询模糊查询空值查询 对结果排序筛选分页结果 Update(修改&…...

C++快速入门 - 2(几分钟让你快速入门C++)
C快速入门 - 2 1. 内联函数1.1 概念1.2 特性 2. auto关键字(C11)2.1 类型别名思考2.2 auto简介2.3 auto的使用细则2.4 auto不能推导的场景 3. 基于范围的for循环(C11)3.1 范围for的语法3.2 范围for的使用条件 1. 内联函数 1.1 概念 以inline修饰的函数叫做内联函数,…...

Excel自定义函数提取超链接
通过自定义函数的方法,批量提取超链接 首选开启开发工具选项 文件-选项-自定义功能区-勾选开发工具选项-确认 AltF11或者直接点击跳转到开发工具-Visual Basic 在左上方VBA project空白处右键点击空白区域-插入-模块 在弹出的窗口中输入以下命令定义GetURL函数 F…...

计算矩阵边缘元素之和
Description 输入一个整数矩阵,计算位于矩阵边缘的元素之和。所谓矩阵边缘的元素,就是第一行和最后一行的元素以及第一列和最后一列的元素。 Input 第一行分别为矩阵的行数m和列数n(m<100,n<100),…...