【数据结构-队列 二】【单调队列】滑动窗口最大值

news2024/9/27 21:23:26

废话不多说,喊一句号子鼓励自己:程序员永不失业,程序员走向架构!本篇Blog的主题是【单调队列】,使用【队列】这个基本的数据结构来实现,这个高频题的站点是:CodeTop,筛选条件为:目标公司+最近一年+出现频率排序,由高到低的去牛客TOP101去找,只有两个地方都出现过才做这道题(CodeTop本身汇聚了LeetCode的来源),确保刷的题都是高频要面试考的题。

在这里插入图片描述

名曲目标题后,附上题目链接,后期可以依据解题思路反复快速练习,题目按照题干的基本数据结构分类,且每个分类的第一篇必定是对基础数据结构的介绍

滑动窗口最大值【HARD】

还是一道经典应用题

题干

在这里插入图片描述

解题思路

对于每个滑动窗口,我们可以使用 O(k)的时间遍历其中的每一个元素,找出其中的最大值。对于长度为 n的数组nums 而言,窗口的数量为 n−k+1,因此该算法的时间复杂度为 O((n−k+1)k)=O(nk),会超出时间限制,因此我们需要进行一些优化。我们可以想到,对于两个相邻(只差了一个位置)的滑动窗口,它们共用着 k−1 个元素,而只有 1 个元素是变化的。我们可以根据这个特点进行优化

  • 在上述滑动窗口形成及移动的过程中,我们注意到元素是从窗口的右侧进入的,然后由于窗口大小是固定的,因此多余的元素是从窗口左侧移除的。 一端进入,另一端移除,这不就是队列的性质吗?所以,该题目可以借助队列来求解

  • 设置双端队列为单调递减队列

  • 当窗口未形成时,每次拿新的元素与队尾元素比较,如果大于队尾元素,则原队尾元素从尾部出队

  • 当窗口形成时,队首元素就是最大元素,将其从队列头部出队,并加入结果集

  • 当窗口形成后并继续滑动时,队首元素也要从队列头部出队,下一个最大值结果将出现在下一个滑动窗口中

该题目的求解思路就清晰了,具体如下:

  1. 遍历给定数组中的元素,如果队列不为空且当前考察元素大于等于队尾元素,则将队尾元素移除。直到,队列为空或当前考察元素小于新的队尾元素
  2. 由于数组下标从0开始,因此当窗口右边界right+1大于等于窗口大小k时,意味着窗口形成。此时,队首元素就是该窗口内的最大值。
  3. 队首元素的下标小于滑动窗口左侧边界left时,表示队首元素已经不再滑动窗口内,因此将其从队首移除。

由此思路可以写代码了

代码实现

给出代码实现基本档案

基本数据结构数组
辅助数据结构单调队列
算法
技巧双指针、滑动窗口

其中数据结构、算法和技巧分别来自:

  • 10 个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie 树
  • 10 个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法
  • 技巧:双指针、滑动窗口、中心扩散

当然包括但不限于以上

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param num int整型一维数组
     * @param size int整型
     * @return int整型ArrayList
     */
    public ArrayList<Integer> maxInWindows (int[] num, int size) {
        // 1 如果数组为空或者size小于1则返回空集合
        if (num.length < 1 || size < 1) {
            return new ArrayList<Integer>();
        }

        // 2 定义结果集并及双端单调队列,双端队列存储元素下标
        ArrayList<Integer> result = new ArrayList<Integer>();
        LinkedList<Integer> singleQueue = new  LinkedList<Integer>();

        // 3 开启窗口滑动
        for (int right = 0; right < num.length; right++) {
            // 3-1 如果单调队列不为空且队尾元素小于当前值,则出队
            while (!singleQueue.isEmpty() && num[singleQueue.peekLast()] <= num[right]) {
                singleQueue.pollLast();
            }
            // 当前元素下标入队
            singleQueue.offerLast(right);

            // 3-2 计算队首元素左边界,因为窗口固定大小,所以right向右移动时,left也向右移动
            int left = right - size + 1;
            if (left > singleQueue.peekFirst()) {
                // 如果当前队列中最大值的索引已不在窗口中,则弹出队列
                singleQueue.pollFirst();
            }

            // 3-3 如果right + 1 >= size,则意味着窗口形成,则队首元素即为窗口最大值,首次窗口形成后此判断条件一直成立
            if (right + 1 >= size) {
                result.add(num[singleQueue.peekFirst()]);
            }

        }

        return result;
    }
}

因为单调队列不限制大小,所以每次获取最大值前要先进行判断,当前队首元素还在不在窗口内,不在窗口内要移出去,防止用例过不去
在这里插入图片描述

复杂度分析

时间复杂度:
空间复杂度:

拓展知识:普通队列、单调队列、优先队列、双向队列

普通队列、单调队列、优先队列和双向队列都是队列数据结构,但它们在性质和用途上有一些区别:

  1. 普通队列(Normal Queue)

    • 普通队列是一种基本的队列数据结构,按照先进先出(FIFO)的原则工作。这意味着最早进入队列的元素最早被移出队列。
    • 普通队列通常用于广泛的应用,例如任务调度、BFS(广度优先搜索)算法等,其中重要的是按照元素的到达顺序进行处理。
  2. 单调队列(Monotonic Queue)

    • 单调队列是一种特殊类型的队列,它通常用于维护队列中元素的单调性,可以是单调递增或单调递减。这意味着元素按照一定的顺序排列。
    • 单调队列通常用于解决一些需要寻找局部最大或最小值的问题,例如在滑动窗口问题中,找到滑动窗口中的最大值或最小值。
    • 单调队列可以通过维护单调性,提高一些特定问题的求解效率。
  3. 优先队列(Priority Queue)

    • 优先队列是一种队列数据结构,它根据元素的优先级(或权重)来确定元素的顺序。具有较高优先级的元素在队列中排在前面。
    • 优先队列通常用于解决需要按照优先级处理任务的问题,例如Dijkstra算法、最小堆和最大堆等数据结构都可以用来实现优先队列。
  4. 双向队列(Double-Ended Queue,Deque)

    • 双向队列是一种允许在队列两端进行插入和删除操作的数据结构,可以在队头和队尾同时进行入队和出队操作。
    • 双向队列通常用于一些需要在队列两端进行高效操作的场景,例如实现队列、栈、滑动窗口等。

总结:

  • 普通队列按照FIFO原则工作,适用于广泛的应用。
  • 单调队列用于维护队列中元素的单调性,以解决一些特定问题。
  • 优先队列用于按照元素的优先级来处理任务或元素,适用于需要根据权重或优先级来排序的场景。
  • 双向队列允许在队列两端进行高效的插入和删除操作,适用于需要在队头和队尾同时操作的场景

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

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

相关文章

Springboot整合Druid:数据库密码加密的实现

ps:Springboot项目&#xff0c;为了防止某些人反编译看到yml里面的数据库密码&#xff0c;对密码进行加密处理&#xff0c;隐藏公钥形式。&#xff08;总有人想扒掉你的底裤看看你屁股长什么样&#xff09; 1.引入依赖&#xff08;以前有依赖就不用了&#xff09; 2.找到Druid…

你想知道的测试自动化-概览篇

测试自动化概念整理 协议 JSON Wire Protocol Specification JSON Wire 协议 现已过时的开源协议的端点和有效负载&#xff0c;它是W3C webdriver的先驱。 devtool协议 Chrome DevTools 协议允许使用工具来检测、检查、调试和分析 Chromium、Chrome 和其他基于 Blink 的浏…

轻松驾驭Hive数仓,数据分析从未如此简单!

1 前言 先通过SparkSession read API从分布式文件系统创建DataFrame 然后&#xff0c;创建临时表并使用SQL或直接使用DataFrame API&#xff0c;进行数据转换、过滤、聚合等操作 最后&#xff0c;再用SparkSession的write API把计算结果写回分布式文件系统 直接与文件系统交…

MyLife - Docker安装Redis

Docker安装Redis 个人觉得像reids之类的基础设施在线上环境直接物理机安装使用可能会好些。但是在开发测试环境用docker容器还是比较方便的。这里学习下docker安装redis使用。 1. Redis 镜像库地址 Redis 镜像库地址&#xff1a;https://hub.docker.com/_/redis/tags 这里是官方…

四向穿梭车智能机器人|HEGERLS托盘式四向穿梭车系统的换轨技术和故障恢复功能

随着物流行业的迅猛发展&#xff0c;托盘四向穿梭式立体库因其在流通仓储体系中所具有的高效密集存储功能优势、运作成本优势与系统化智能化管理优势&#xff0c;已发展为仓储物流的主流形式之一。托盘四向穿梭车立体仓库有全自动和半自动两种工作模式&#xff0c;大大提高了货…

java基础 异常

异常概述&#xff1a; try{ } catch{ }&#xff1a; package daysreplace;import com.sun.jdi.IntegerValue;import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.…

pymoo包NSGA2算法实现多目标遗传算法调参详细说明

pymoo包NSGA2算法实现多目标遗传算法调参详细说明 1.定义待求解问题1.0定义问题的参数说明1.0.0 求解问题必须设置在def _evaluate(self, x, out, *args, **kwargs)函数中1.0.1 问题必须用 out["F"] [f1, f2] 包裹起来1.0.2 约束条件也必须用 out["G"] […

Oracle 简介与 Docker Compose部署

最近&#xff0c;我翻阅了在之前公司工作时的笔记&#xff0c;偶然发现了一些有关数据库的记录。当初&#xff0c;我们的项目一开始采用的是 Oracle 数据库&#xff0c;但随着项目需求的变化&#xff0c;我们不得不转向使用 SQL Server。值得一提的是&#xff0c;公司之前采用的…

Windows保姆级安装Docker教程

1.官网下载 2.安装 3.启动Hyper-V 4.检查是否安装成功 1.下载 1.1.打开官网&#xff0c;然后点击下载 官网链接&#xff1a;https://hub.docker.com/ 2.安装 下载好之后会得到一个exe程序&#xff0c;然后启动它&#xff0c;进行安装。 去掉 WSL 不使用Hyper-V&#xff0…

KdMapper扩展实现之REALiX(hwinfo64a.sys)

1.背景 KdMapper是一个利用intel的驱动漏洞可以无痕的加载未经签名的驱动&#xff0c;本文是利用其它漏洞&#xff08;参考《【转载】利用签名驱动漏洞加载未签名驱动》&#xff09;做相应的修改以实现类似功能。需要大家对KdMapper的代码有一定了解。 2.驱动信息 驱动名称hwin…

使用testMe自动生成单元测试用例

文章目录 1、testMe简介2、插件对比2.1 testMe2.2 Squaretest2.3 Diffblue 3、IDEA插件安装4、单测用例4.1 maven依赖4.2 生成用例 5、自定义模板6、使用自定义模板生成用例7、调试用例 1、testMe简介 公司对于系统单元测试覆盖率有要求&#xff0c;需要达到50%或80%以上才可以…

RV1126-RV1109-进入uboot的按键和名字显示-HOSTNAME

今天添加一个小功能,就是uboot是按CTRLC进入的 今日我做了一个定制,让按L或者l让也进入uboot指令模式,并且修改主板名字显示 默认是CTRLC:键码值是0x03(ASCII对照表) 于是代码中跟踪: //rv1126_rv1109/u-boot/common/console.c int ctrlc(void) { #ifndef CONFIG_SANDBOXif (…

Python大数据之Python进阶(五)线程

文章目录 线程1. 线程的介绍2. 线程的概念3. 线程的作用4. 小结 线程 学习目标 能够知道线程的作用 1. 线程的介绍 在Python中&#xff0c;想要实现多任务除了使用进程&#xff0c;还可以使用线程来完成&#xff0c;线程是实现多任务的另外一种方式。 2. 线程的概念 线程是进程…

【chrome基础】Chrome、Chromium、libcef、electron版本关系大揭秘!

文章目录 概述chrome、Chromium、cef、electron 版本管理chrome的各种概念和学习资料V8 bindings 设计谷歌V8引擎探秘&#xff1a;基础概念Chrome 的插件&#xff08;Plugin&#xff09;与扩展&#xff08;Extension&#xff09;Chrome插件开发 概述 Chrome、Chromium、libcef、…

电荷泵CP原理及在PLL/DLL中的使用

参考【模拟集成电路】电荷泵&#xff08;CP&#xff09;设计_pll 电荷泵-CSDN博客 PLL-CP | Fitzs Blog 1.PLL/DLL中电荷泵概念及原理 电荷泵CP(charge pump)是锁相环中重要的一个模块&#xff0c;其主要功能是将鉴频鉴相器 (PFD) 输出的时钟相位差值转化为电荷&#xff0c;将…

UE5如何实现语言本地化管理(中英文切换)

一。实现蓝图的本地化控制 1.打开本地化控制面版 2.设置收集文本的路径 3.添加自己需要使用的语言&#xff0c;一般是中文 4.收集文本并进行转换语言的翻译 5.进入面板之后开始翻译 6.翻译完成之后计算字数并编译 7。一整套流程下来就是这样了 8.编译完成之后会在文件中生成…

一文汇总 Linux 内核调试的方法

内核开发比用户空间开发更难的一个因素就是内核调试艰难。内核错误往往会导致系统宕机&#xff0c;很难保留出错时的现场。调试内核的关键在于你的对内核的深刻理解。 在调试一个bug之前&#xff0c;我们所要做的准备工作有&#xff1a; 有一个被确认的bug&#xff0c;包含这…

10.10泊松、指数、伽马分布的理解

泊松定理、泊松分布 泊松定理就是描述在T段时间内每时每刻一直进行一个实验&#xff0c;这个实验成功的概率由t段时间总的成功期望次数决定&#xff0c;就是给二项分布加了个时间 泊松分布可用来描述某段时间来电次数的分布&#xff0c;电话台收到的呼叫数&#xff0c;商城的…

2023年【陕西省安全员B证】最新解析及陕西省安全员B证操作证考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年陕西省安全员B证最新解析为正在备考陕西省安全员B证操作证的学员准备的理论考试专题&#xff0c;每个月更新的陕西省安全员B证操作证考试祝您顺利通过陕西省安全员B证考试。 1、【多选题】《陕西省建设工程质量…

导入导出Excel

一、Springboot Easyexcel读取写入数据&#xff0c;多头行数&#xff0c;多sheet&#xff0c;复杂表头简单实现 1. 导入依赖&#xff0c;阿里的easyexcel插件 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId>…