Java怎么实现滑动窗口的最大值

发布时间:2022-02-21 16:32:21 作者:iii
来源:亿速云 阅读:198
# Java怎么实现滑动窗口的最大值

## 引言

滑动窗口最大值问题(Sliding Window Maximum)是算法领域的一个经典问题,常见于大数据处理、实时监控等场景。给定一个整数数组和一个固定大小的窗口,窗口从数组的最左端滑动到最右端,每次窗口滑动一个位置,要求找出每个窗口中的最大值。

本文将详细探讨在Java中实现滑动窗口最大值的多种方法,分析其时间复杂度与空间复杂度,并提供完整的代码示例。

---

## 问题描述

给定一个整数数组 `nums` 和一个整数 `k`(表示窗口大小),需要返回所有滑动窗口中的最大值组成的数组。

**示例:**
```java
输入: 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) {
    if (nums == null || nums.length == 0 || k <= 0) {
        return new int[0];
    }
    int n = nums.length;
    int[] result = new int[n - k + 1];
    for (int i = 0; i <= n - k; i++) {
        int max = Integer.MIN_VALUE;
        for (int j = i; j < i + k; j++) {
            max = Math.max(max, nums[j]);
        }
        result[i] = max;
    }
    return result;
}

复杂度分析


方法二:使用双端队列(Deque)

思路

通过维护一个双端队列,存储当前窗口中可能成为最大值的元素的索引。队列中的元素按从大到小排序,且保证队列头部始终是当前窗口的最大值。

实现步骤

  1. 遍历数组,维护一个双端队列。
  2. 移除队列中不在当前窗口范围内的元素。
  3. 移除队列尾部小于当前元素的索引,确保队列单调递减。
  4. 将当前元素索引加入队列尾部。
  5. 当窗口形成时,记录队列头部的最大值。

实现代码

import java.util.Deque;
import java.util.LinkedList;

public int[] maxSlidingWindow(int[] nums, int k) {
    if (nums == null || nums.length == 0 || k <= 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++) {
        // 移除不在窗口范围内的元素
        while (!deque.isEmpty() && deque.peekFirst() < i - k + 1) {
            deque.pollFirst();
        }
        // 移除队列尾部小于当前元素的索引
        while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
            deque.pollLast();
        }
        deque.offerLast(i);
        // 当窗口形成时记录最大值
        if (i >= k - 1) {
            result[i - k + 1] = nums[deque.peekFirst()];
        }
    }
    return result;
}

复杂度分析


方法三:动态规划

思路

将数组分成大小为k的块,预处理每个块的从左到右和从右到左的最大值数组。最后通过组合这两个数组快速计算任意窗口的最大值。

实现步骤

  1. 构建从左到右的块最大值数组 left
  2. 构建从右到左的块最大值数组 right
  3. 对于任意窗口 [i, i+k-1],其最大值为 max(right[i], left[i+k-1])

实现代码

public int[] maxSlidingWindow(int[] nums, int k) {
    if (nums == null || nums.length == 0 || k <= 0) {
        return new int[0];
    }
    int n = nums.length;
    int[] left = new int[n];
    int[] right = new int[n];
    left[0] = nums[0];
    right[n - 1] = nums[n - 1];
    for (int i = 1; i < n; i++) {
        left[i] = (i % k == 0) ? nums[i] : Math.max(left[i - 1], nums[i]);
        int j = n - i - 1;
        right[j] = (j % k == 0) ? nums[j] : Math.max(right[j + 1], nums[j]);
    }
    int[] result = new int[n - k + 1];
    for (int i = 0; i <= n - k; i++) {
        result[i] = Math.max(right[i], left[i + k - 1]);
    }
    return result;
}

复杂度分析


方法对比

方法 时间复杂度 空间复杂度 适用场景
暴力法 O(n*k) O(1) 小规模数据
双端队列 O(n) O(k) 通用场景,推荐使用
动态规划 O(n) O(n) 需要频繁查询任意窗口

实际应用示例

假设需要实时监控股票价格在过去k天的最高值,滑动窗口最大值算法可以高效生成结果:

int[] stockPrices = {100, 80, 60, 70, 60, 75, 85};
int k = 3;
int[] maxInWindow = maxSlidingWindow(stockPrices, k);
// 输出: [100, 80, 70, 75, 85]

总结

本文介绍了三种Java实现滑动窗口最大值的方法: 1. 暴力法:简单但效率低,适用于小数据量。 2. 双端队列:高效且通用,推荐作为首选。 3. 动态规划:适合需要频繁查询的场景,但空间占用较高。

根据实际需求选择合适的方法,双端队列在大多数情况下是最优解。


参考文献

”`

注:实际字数约为1800字,可通过扩展示例或优化分析部分补充至2050字。

推荐阅读:
  1. 利用java如何实现获取List集合的最大值
  2. 使用Java如何实现求最大值

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java

上一篇:Java中String字符串数据类型如何使用

下一篇:Java怎么实现九九乘法表

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》