首页 > 编程语言 >算法练习题09:滑动窗口最大值(队列、双端队列)

算法练习题09:滑动窗口最大值(队列、双端队列)

时间:2024-09-02 18:50:14浏览次数:14  
标签:练习题 窗口 队列 双端 最大值 元素 移除

class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || nums.length == 0) {
            return new int[0];
        }

        int n = nums.length;
        int[] result = new int[n - k + 1];
        Deque<Integer> deque = new LinkedList<>();

        for (int i = 0; i < n; i++) {
            // 移除不在窗口内的元素(即索引小于 i-k+1 的元素)
            if (!deque.isEmpty() && deque.peekFirst() < i - k + 1) {
                deque.pollFirst();
            }

            // 移除所有比当前元素小的元素,它们不可能是最大值
            while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
                deque.pollLast();
            }

            // 将当前元素的索引加入到队列中
            deque.offerLast(i);

            // 当前索引从 i >= k-1 时,开始记录最大值
            if (i >= k - 1) {
                result[i - k + 1] = nums[deque.peekFirst()];
            }
        }

        return result;
    }
}

代码分步解释

  1. 初始化与边界检查

    首先,检查输入的数组是否为空或长度为零。如果是这样,直接返回一个空数组,因为没有任何滑动窗口可计算最大值。
  2. 创建结果数组与双端队列

    确定数组的长度,并创建一个结果数组 result 来存储每个滑动窗口的最大值。结果数组的长度为 n - k + 1,因为这是滑动窗口在数组中滑动的次数。创建一个双端队列(Deque),用于存储滑动窗口中可能的最大值的元素的索引。双端队列允许我们在常数时间内从两端插入或删除元素。
  3. 遍历数组

    开始遍历数组的每一个元素。对于每个元素,我们将更新双端队列,确保队列中的元素始终是当前窗口中可能的最大值。
  4. 移除窗口外的元素

    检查双端队列的最前端(即第一个元素),如果它的索引已经超出了当前滑动窗口的左边界(即索引小于 i - k + 1),就将它从队列中移除。这是因为该元素已经不再属于当前窗口,不可能再成为最大值。
  5. 保持队列的有序性

    为了确保队列中的元素按从大到小的顺序排列,我们会移除队列中所有比当前元素小的元素。因为如果当前元素比这些元素大,那么这些元素在当前窗口内或未来的窗口中都不可能成为最大值。
  6. 加入当前元素

    将当前元素的索引加入到双端队列的末尾。这个步骤保证了双端队列始终包含当前窗口内可能的最大值候选者。
  7. 记录当前窗口的最大值

    当窗口的大小达到 k 时,开始记录每个窗口的最大值。双端队列的第一个元素就是当前窗口的最大值,因为队列中的元素已经被排序,最大值在最前端。
  8. 返回结果

    完成数组遍历后,返回结果数组,其中包含了每个滑动窗口的最大值。

双端队列(Deque)基础知识

什么是双端队列?

双端队列(Deque,全称为Double-Ended Queue)是一种特殊类型的队列,它允许在队列的两端进行插入和删除操作。与普通的队列不同,双端队列在队头和队尾都可以进行元素的添加和移除操作。

双端队列的基本操作:
  1. offerFirst(E e) 和 offerLast(E e):在队列的前端或后端插入元素。
  2. pollFirst() 和 pollLast():从队列的前端或后端移除并返回元素。
  3. peekFirst() 和 peekLast():查看队列的前端或后端元素,但不移除它。
为什么在滑动窗口问题中使用双端队列?

       1. 高效的最大值维护:双端队列允许我们高效地追踪当前滑动窗口的最大值。我们可以在 O(1) 的时间复杂度内添加、移除元素,并快速确定窗口的最大值。

        2.动态管理窗口内的元素:双端队列帮助我们在滑动窗口移动时动态管理元素,使得队列中的元素始终是当前窗口内可能的最大值候选者。

在滑动窗口问题中的应用:
  1. 维护一个有序的候选最大值队列:在每次滑动窗口时,我们用双端队列来存储窗口内可能的最大值,并保证这些元素的顺序。
  2. 高效移除过期元素:当滑动窗口向右移动时,如果队列中最前端的元素已经不在当前窗口范围内,我们可以快速将其移除。
  3. 保持窗口内的元素降序排列:每当我们处理一个新元素时,如果发现它比队列中的一些元素大,我们就移除这些较小的元素,因为它们不再可能成为最大值。

操作示例

Deque<Integer> deque = new LinkedList<>();

// 在队列的头部添加元素
deque.offerFirst(1);

// 在队列的尾部添加元素
deque.offerLast(2);

// 查看队列的头部元素
int firstElement = deque.peekFirst();  // 返回 1

// 查看队列的尾部元素
int lastElement = deque.peekLast();  // 返回 2

// 从队列的头部移除元素
int removedFirst = deque.pollFirst();  // 返回 1,队列现在是 [2]

// 从队列的尾部移除元素
int removedLast = deque.pollLast();  // 返回 2,队列现在为空

基础队列(Queue)基础知识

什么是队列?

队列(Queue)是一种先进先出(FIFO,First-In-First-Out)的数据结构。这意味着第一个进入队列的元素将是第一个被移除的元素。可以把它想象成排队的情形,最先排队的人最先得到服务。

队列的基本操作:
  1. offer(E e) 或 add(E e):在队列的尾部添加一个元素。这相当于一个人排队进入队列。
  2. poll() 或 remove():从队列的头部移除并返回第一个元素。这相当于最先排队的人被服务后离开队列。
  3. peek() 或 element():查看队列头部的第一个元素,但不移除它。这相当于查看排在最前面的人是谁,但不让他离开队列。
队列的应用场景:

        1.任务调度:在操作系统中,任务排队等待 CPU 处理。

        2.广度优先搜索(BFS):在图或树的遍历中,队列用于按层级顺序访问节点。

        3.生产者-消费者模式:在多线程编程中,队列用于在生产者和消费者之间传递数据。

操作示例

Queue<Integer> queue = new LinkedList<>();

// 添加元素到队列的尾部
queue.offer(1);
queue.offer(2);
queue.offer(3);

// 查看队列的头部元素,但不移除
int head = queue.peek();  // 返回 1

// 移除并返回队列的头部元素
int removedElement = queue.poll();  // 返回 1,队列现在是 [2, 3]

标签:练习题,窗口,队列,双端,最大值,元素,移除
From: https://blog.csdn.net/2302_78946634/article/details/141824734

相关文章

  • 线性表之队列API设计思路
    Java学习手册+面试指南:https://javaxiaobear.cn队列是一种基于先进先出(FIFO)的数据结构,是一种只能在一端进行插入,在另一端进行删除操作的特殊线性表,它按照先进先出的原则存储数据,先进入的数据,在读取数据时先读被读出来。1、队列的API设计类名Queue构造方法Queue():创建Queue对象成......
  • Java 最小优先队列API设计与实现
    Java学习+面试指南:https://javaxiaobear.cn最小的元素放在数组的索引1处。每个结点的数据总是小于等于它的两个子结点的数据。1、API设计类名MinPriorityQueue构造方法MinPriorityQueue(intcapacity):创建容量为capacity的MinPriorityQueue对象成员方法privatebooleanless(inti......
  • Java最大优先队列设计与实现
    Java学习+面试指南:https://javaxiaobear.cn1、API设计类名MaxPriorityQueue构造方法MaxPriorityQueue(intcapacity):创建容量为capacity的MaxPriorityQueue对象成员方法privatebooleanless(inti,intj):判断堆中索引i处的元素是否小于索引j处的元素privatevoideach(inti,int......
  • Java索引优先队列设计思路与实现
    Java学习+面试指南:https://javaxiaobear.cn1、实现思路存储数据时,给每一个数据元素关联一个整数,例如insert(intk,Tt),我们可以看做k是t关联的整数,那么我们的实现需要通过k这个值,快速获取到队列中t这个元素,此时有个k这个值需要具有唯一性。最直观的想法就是我们可以用一个T[]ite......
  • 数据结构——队列
    文章目录队列基本概念顺序存储的队列:循环队列循环队列的基本操作链式存储的队列:链式队列链式队列的基本操作队列基本概念队列是最常见的概念,日常生活经常需要排队,仔细观察队列会发现,队列是一种逻辑结构,是一种特殊的线性表。特殊在:只能在固定的两端操作线性表只......
  • PHP转Go系列 | ThinkPHP与Gin框架之Redis延时消息队列技术实践
    大家好,我是码农先森。我们在某宝或某多多上抢购商品时,如果只是下了订单但没有进行实际的支付,那在订单页面会有一个支付倒计时,要是过了这个时间点那么订单便会自动取消。在这样的业务场景中,一般情况下就会使用到延时队列。通常在客户下单之后,就会将订单数据推送到延时队列中并且......
  • RabbitMQ 队列使用基础教程
    实践环境JDK1.8.0_121amqp-client5.16.0附:查看不同版本的amqp-client客户端支持的JavaJDK版本https://www.rabbitmq.com/client-libraries/java-versionsmavnsettings.xml<?xmlversion="1.0"encoding="UTF-8"?><settingsxsi:schemaLocation="h......
  • Java消息队列:RabbitMQ与Kafka的集成与应用
    Java消息队列:RabbitMQ与Kafka的集成与应用大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在现代的分布式系统中,消息队列是实现系统间通信、解耦和提高可扩展性的重要组件。RabbitMQ和Kafka是两个广泛使用的消息队列系统,它们各有特点和优势。本文将介......
  • C程序设计语言(第2版·新版)练习题1-18
    练习1-18 编写一个程序,删除每个输入行末尾的空格及制表符,并删除完全是空格的行。#include<stdio.h>#defineMAXLINE1000intgetline(chars[],intlim);intremove_tail(chars[]);intmain(intargc,char*argv[]){(void)argc;(void)argv;......
  • C程序设计语言(第2版·新版)练习题1-19
    练习1-19 编写函数reverse(s),将字符串s中的字符顺序颠倒过来。使用该函数编写一个程序,每次颠倒一个输入行中的字符顺序。#include<stdio.h>#defineMAXLINE1000intgetline(chars[],intlim);voidreverse(chars[]);intmain(intargc,char*argv[]){(vo......