首页 > 试题广场 >

滑动窗口的最大值

[编程题]滑动窗口的最大值
  • 热度指数:599837 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
给定一个长度为 n 的数组 num 和滑动窗口的大小 size ,找出所有滑动窗口里数值的最大值。

例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

窗口大于数组长度或窗口长度为0的时候,返回空。

数据范围: ,数组中每个元素的值满足 
要求:空间复杂度 ,时间复杂度


示例1

输入

[2,3,4,2,6,2,5,1],3

输出

[4,4,6,6,6,5]
示例2

输入

[9,10,9,-7,-3,8,2,-6],5

输出

[10,10,9,8]
示例3

输入

[1,2,3,4],5

输出

[]
推荐
import java.util.*;
/**
用一个双端队列,队列第一个位置保存当前窗口的最大值,当窗口滑动一次
1.判断当前最大值是否过期
2.新增加的值从队尾开始比较,把所有比他小的值丢掉
*/
public class Solution {
   public ArrayList<Integer> maxInWindows(int [] num, int size)
    {
        ArrayList<Integer> res = new ArrayList<>();
       	if(size == 0) return res;
		int begin;	
		ArrayDeque<Integer> q = new ArrayDeque<>();
		for(int i = 0; i < num.length; i++){
			begin = i - size + 1;
			if(q.isEmpty())
				q.add(i);
			else if(begin > q.peekFirst())
                q.pollFirst();
		
			while((!q.isEmpty()) && num[q.peekLast()] <= num[i])
                q.pollLast();
			q.add(i);	
			if(begin >= 0)
				res.add(num[q.peekFirst()]);
		}
		return res;
    }
}

编辑于 2015-09-02 14:11:26 回复(51)
暴力解法 + 顶堆
import java.util.*;
public class Solution {
    public ArrayList<Integer> maxInWindows (int[] num, int size) {
        if (size > num.length || size < 1)
            return new ArrayList<Integer>();
        // 维护一个大小为size的大顶堆,每个窗口取出堆顶元素
        ArrayList<Integer> result = new ArrayList<>();
        int left = 0;
        int right = size - 1;
        int len = num.length;
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>((o1,o2)->o2-o1);
        // 数组左端与堆左端对齐
        for(int i=0;i<size;i++)
            maxHeap.add(num[i]);
        
        // 堆顶元素添加到result
        result.add(maxHeap.peek());          // {2,3,4,2,6,2,5,1}

        // 数组往左移
        while(left <= len - size && right < len - 1){

            // 删除窗口最左边的元素
            maxHeap.remove(num[left]);

            right++;
            left++;

            // 添加之后的元素
            maxHeap.add(num[right]);
            result.add(maxHeap.peek());
        }
        return result;
    }
        //* 暴力解法
        public ArrayList<Integer> maxInWindows (int[] num, int size) {
        if(size > num.length || size<=0)
            return new ArrayList<Integer>();
        // 窗口左端
        int left = 0;
        // 窗口右端
        int right = size - 1;

        int len = num.length;

        ArrayList<Integer> result = new ArrayList<Integer>();

        while(right <= len-1 && left <= len - size){
            int max = num[left]; // 假设当前最大的数是 《当前窗口第0个元素》
            // 找出当前窗口最大的数max
            for(int i = left;i <= right;i++){
                if(num[i]>=max){
                    max = num[i];
                }
            }
            result.add(max);
            // 移动窗口
            left++;
            right++;
        }
        return result;
    }
}


发表于 2024-11-04 21:34:17 回复(0)
import java.util.*;

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param num int整型一维数组
     * @param size int整型
     * @return int整型ArrayList
     */
    public ArrayList<Integer> maxInWindows (int[] num, int size) {
        // write code here
        int len = num.length;
        ArrayList<Integer> list = new ArrayList<>();
        if(size>len||size==0) return list;
        for(int i =0;i<len-size+1;i++){
            int max = num[i];
            for(int j = 1;j<size;j++){
                max = Math.max(max,num[i+j]);
            }

            list.add(max);
        }
        return list;
    }
}
发表于 2024-09-22 17:47:49 回复(0)
闹麻了,这样空间复杂度是O(n^2)
import java.util.*;

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param num int整型一维数组
     * @param size int整型
     * @return int整型ArrayList
     */
    public ArrayList<Integer> maxInWindows (int[] num, int size) {
        // write code here
        ArrayList<Integer> result = new ArrayList<Integer>();
        if(num == null || size > num.length || size == 0){
            return result;
        }
        int index = 0;
        while(index <= num.length - size){
            int max = Integer.MIN_VALUE;
            for(int i = index; i < index + size; i++){
                if(num[i] > max){
                    max = num[i];
                }
            }
            result.add(max);
            // max = Integer.MIN_VALUE;
            index++;
        }
        return result;
    }
}
发表于 2024-09-16 20:25:30 回复(0)
维护一个双端队列
import java.util.*;

public class Solution {
    public ArrayList<Integer> maxInWindows (int[] num, int size) {
        ArrayList<Integer> res = new ArrayList<>();
        // 如果滑动窗口的大小等于0或者大于num的长度,则返回空列表
        if (size == 0 || size > num.length) {
            return res;
        }
        // 维护一个双端队列,队列的最大长度为size(可以小于size)
        // 这个队列每次都会往头部放入当前元素,如果当前元素大于等于队列里的最大值,则清空队列后,只添加这个值
        // 如果当前元素小于队列里的最大值,则直接添加到头部
        // 双端队列的长度达到边界值size时,先加入(队列中靠后)的值需要逐个比较,留下靠前的最大(靠后的小值后面必定用不到)
        Deque<Integer> deque = new ArrayDeque<>();
        int maxVal = 0;
        // 初始化第一个滑动窗口的队列
        for (int i = 0; i < size; i++){
            while (!deque.isEmpty() && num[i] >= deque.peekLast()) {
                deque.pollLast();
            }
            deque.offerFirst(num[i]);
        }
        res.add(deque.peekLast());

        for(int i = size; i < num.length; i++) {
            // 去掉滑动窗口外的值
            if(deque.size() >= size) {
                deque.pollLast();
            }
            // 在头部加入当前值
            deque.offerFirst(num[i]);
            // 从队列尾部遍历,这里主要完成两部分任务:
            // 1. 如果当前(队列头部)值比所有值都要大,则当前值之前的数都不要
            // 2. 在队列尾部,找到靠前的大值才停止删除元素,例如 头-4-6-2-1-尾,这里2和1都在的时候6必定在,所以必然不可能是最小值,因此删除
            // 以上操作可以保证在找最大值的时候,只需要找队列尾部元素即可
            // (可能有更简洁的写法,我这里不做优化了)
            while (deque.size() >= 2) {
                int temp = deque.pollLast();
                if (temp < deque.peekFirst()) {
                    continue;
                }
                if (temp > deque.peekLast()){
                    deque.offerLast(temp);
                    break;
                }
            }
            res.add(deque.peekLast());
        }
        return res;
    }
}

发表于 2024-09-08 20:20:20 回复(0)
public ArrayList<Integer> maxInWindows (int[] num, int size) {
        ArrayList<Integer> r = new ArrayList<>();
        if(num.length < size || size == 0) return r;
        int max = Integer.MIN_VALUE;
        for(int i=0;i<num.length-size+1;i++){
            if(max == Integer.MIN_VALUE){
                for(int j=0;j<size;j++){
                    if(max < num[i+j]) max = num[i+j];
                }
            }else if(max < num[i+size-1]){
                max = num[i+size-1];
            }
            r.add(max);
            if(num[i] == max) max = Integer.MIN_VALUE;
        }
        return r;
    }

发表于 2024-08-29 16:30:23 回复(0)
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param num int整型一维数组
     * @param size int整型
     * @return int整型ArrayList
     */
    public ArrayList<Integer> maxInWindows (int[] num, int size) {
        // write code here
        // 核心思想:设置一个【单调双端队列】,确保队头是当前窗口内的最大值

        // 0.判断特殊情况
        ArrayList<Integer> result = new ArrayList<>();
        if (size <= 0 || size > num.length) {
            return result;
        }

        // 1.创建双端队列
        Deque<Integer> dq = new LinkedList<>();

        // 2.录入初始窗口
        int h = 0;
        int t = size - 1;
        for (int i = h; i <= t; i++) {
            // 录入的是下标
            inDequeue(num, i, dq);
        }

        // 3.得到初始窗口的结果
        // 注意!从队列中取出来的是下标值,要转换成元素值存入结果中
        result.add(num[dq.peekFirst()]);

        // 4.模拟窗口移动-h右移且t右移,不断得到结果
        for (int i = 1; i <= (num.length - size); i++) {
            // 4.1 t后移,新元素入队
            inDequeue(num, ++t, dq);
            // 4.2 h后移,旧元素出队
            outDequeue(num, h++, dq);
            // 4.3 新窗口形成,从队头获得当前窗口最大元素
            // 注意!从队列中取出来的是下标值,要转换成元素值存入结果中
            result.add(num[dq.peekFirst()]);
        }

        return result;
    }

    // 新元素入单调双端队列的操作
    public void inDequeue(int[] num, int i, Deque<Integer> dq) {
        // 录入的是下标
        if (dq.isEmpty()) {
            // 若队列为空,则直接录入
            dq.addLast(i);
        }
        else if (num[i] < num[dq.peekLast()]) {
            // 若待录入元素小于队尾元素,则入队
            dq.addLast(i);
        } else {
            // 若待录入元素大于等于队尾元素,则不断地让堆尾元素出队,直到满足小于队尾元素入队
            // 等于也要出队的目的是让新的元素替代队列中旧的元素
            while (!dq.isEmpty() && num[i] >= num[dq.peekLast()]) {
                dq.pollLast();
            }
            // 此时队列空了,或者队尾下标元素大于当前待入队元素
            dq.addLast(i);
        }
    }

    // 旧元素出单调双端队列的操作
    public void outDequeue(int[] num, int i, Deque<Integer> dq) {
        // 录入的是下标
        if (i == dq.peekFirst()) {
            // 当待出队元素正好是队头元素(当前窗口最大)时才出队
            dq.pollFirst();
        }
        // 当待出队元素不是队头元素(当前窗口最大)时不用出队
    }
}

发表于 2024-08-28 13:46:45 回复(0)
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param num int整型一维数组
     * @param size int整型
     * @return int整型ArrayList
     */
    public ArrayList<Integer> maxInWindows (int[] num, int size) {
        int n = num.length;
        if (size == 0 || size > n) {
            return new ArrayList<Integer>();
        }
        PriorityQueue<Integer> q = new PriorityQueue<>((a, b)-> {
            return b - a;
        });
        ArrayList<Integer> res = new ArrayList<>();

        int i = 0;
        for (int j = 0; j < n; j++) {
            q.add(num[j]);
            if (j - i + 1 == size) {
                res.add(q.peek());
            }
            if (j - i + 1 == size) {
                q.remove(num[i]);
                i++;
            }
        }
        return res;
    }
}
发表于 2024-08-02 23:03:56 回复(0)

模板题hh

import java.util.*;

public class Solution {
    public ArrayList<Integer> maxInWindows (int[] num, int size) {
        ArrayList<Integer> res = new ArrayList<>();
        if (size == 0 || size > num.length) return res;
        // 队列存的索引,用以方便最大值滑出窗口
        int[] q = new int[10010];
        int hh = 0, tt = -1;
        // 枚举窗口右端点
        for (int i = 0; i < num.length; i++) {
            // 维护单调队列 最值可能已滑出窗口,基于索引,如果最值索引和右端点算出的窗口大小超过 size 那就说明最值索引已经出窗口了,需要剔除
            while (hh <= tt && i - q[hh] + 1 > size) hh++;
            // 将新入元素维护到单调队列中 (第一个比它大的元素之后,注意队列维护索引值!)
            while (hh <= tt && num[q[tt]] <= num[i]) tt--;
            q[++tt] = i;
            // 枚举到窗口大小时,开始收集最值
            if (i + 1 >= size) res.add(num[q[hh]]);
        }
        return res;
    }
}
发表于 2024-07-24 09:38:52 回复(0)
import java.util.*;

public class Solution {
    public ArrayList<Integer> maxInWindows (int[] num, int size) {
        ArrayList<Integer> dp = new ArrayList<>();
        if(size>num.length||size==0) return dp;
     for(int i=0;i<num.length-size+1;i++){
        int max = num[i];
        for(int j=1;j<size;j++){
            if(num[i+j]>max){
                max = num[i+j];
            }
        }
        dp.add(max);
     }
     return dp;
    }
}
编辑于 2024-03-28 20:50:46 回复(0)
略微优化的暴力求解。记录最大值在窗口内的位置。
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param num int整型一维数组 
     * @param size int整型 
     * @return int整型ArrayList
     */
    public ArrayList<Integer> maxInWindows (int[] num, int size) {
        // write code here
        ArrayList<Integer> list = new ArrayList<>();
        int max = 0;
        int count = 0;
        if(num.length ==0 || size > num.length || size ==0) return list;
        for(int i = 0;i<=num.length-size;i++){
            count--;
            if(count < 0){
                max = num[i];
                count = 0;
                for(int j = 1; j<size;j++){
                    if(num[i+j]> max){
                        max = num[i+j];
                        count = j;
                    }
                }
            }else{
                if(num[i+size-1] > max){
                    max = num[i+size-1];
                    count = size-1;
                }
            }
            list.add(max);
        }
        return list;
    }
}


编辑于 2024-01-29 16:34:43 回复(0)
//java双指针解法
import java.util.*;


public class Solution {
    public ArrayList<Integer> maxInWindows (int[] num, int size) {
        //定义内指针
        int i = 0;
        //定义外指针
        int j = 0;
        //记录最大值
        int max = 0;
        //初始化ArrayList来储存结果
        ArrayList<Integer> arr  = new ArrayList<>();
        //如果数组长度小于size或者size为0返回空
        if (num.length<size||size==0) return arr;
        //外指针需要遍历的次数为数组长度-size+1
        for (int index = 0;index< num.length-size+1;index++) {
            //每次将最大值初始化为当前滑块的第一个值
            max = num[i];
            //内循环,遍历滑块中的每一个数
            while (i<size+j-1){
                i++;
                //找到最大值
                if (max<num[i]){
                    max = num[i];
                }
            }
            //遍历完成后外循环指针加1
            j++;
            //将最大值添加到结果中
            arr.add(max);
            //每次循环完成一个滑块后将内指针i的位置移到下一个滑块的第一个位置上
            i = j;
        }
        return arr;
    }
}

编辑于 2024-01-08 14:47:21 回复(0)
为啥这个题目标记成了困难。我觉得很简单啊。从头遍历指定长度的字数组,拿到每个子数组的最大值,然后就ok了啊
发表于 2023-12-15 21:10:51 回复(1)
public class Solution {
    /**
     * 给定一个长度为 n 的数组 num 和滑动窗口的大小 size ,找出所有滑动窗口里数值的最大值。
     *
     * 双端队列保存最大值的下标
     *
     * @param num int整型一维数组
     * @param size int整型
     * @return int整型ArrayList
     */
    public ArrayList<Integer> maxInWindows (int[] num, int size) {
        ArrayList<Integer> result = new ArrayList<>();
        if (size > num.length || size == 0) {
            return result;
        }
        ArrayDeque<Integer> que = new ArrayDeque<>();
        que.offerFirst(0);
        for (int i = 0; i < num.length; i++) {
            //当前元素比头更大,队列清空,保存下标
            if (num[i] >= num[que.peekFirst()]) {
                que.clear();
                que.offerFirst(i);
            }
            //比头小,也比尾小,下标置尾
            else if (num[i] < num[que.peekLast()]) {
                que.offerLast(i);
            }
            //比头小,比尾大,删除原来的尾部,再下标置尾
            else {
                que.pollLast();
                que.offerLast(i);
            }
            //检查下标元素是否在窗口,不在,则头部下标移除
            if (que.peekFirst() <= i - size) {
                que.pollFirst();
            }
            //窗口饱和开始记录最大值
            if (i >= size - 1) {
                result.add(num[que.peekFirst()]);
            }
        }
        return result;
    }

}

发表于 2023-10-30 13:11:02 回复(0)
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param num int整型一维数组
     * @param size int整型
     * @return int整型ArrayList
     */
    public ArrayList<Integer> maxInWindows (int[] num, int size) {
        int i = 0;
        int j = i + size - 1;
        int preMaxIndex = -1, maxIndex = -1;
        int preMax = Integer.MIN_VALUE, max = Integer.MAX_VALUE;
        ArrayList<Integer> arrayList = new ArrayList<>();
        for (int index = i; index <= j && j < num.length; index++) {
            if (maxIndex >= i && maxIndex <= j) {
                if (num[j] > max) {
                    max = num[j];
                    maxIndex = j;
                }
                arrayList.add(max);
                index = ++i - 1;
                j++;
                continue;
            } else if ((maxIndex < i || maxIndex > j) && index == i) {
                preMax = Integer.MIN_VALUE;
            }
            if (preMax < num[index]) {
                preMax = num[index];
                preMaxIndex = index;
            }
            if (index == j) {
                max = preMax;
                maxIndex = preMaxIndex;
                arrayList.add(max);
                index = ++i - 1;
                j++;
            }
        }
        return arrayList;
    }
}

发表于 2023-10-26 06:09:00 回复(0)
public static ArrayList<Integer> maxInWindows(int[] num, int size) {
        int left = 0;
        int right = size - 1;
        ArrayList<Integer> temp = new ArrayList<>();
        ArrayList<Integer> ret = new ArrayList<>();
        while (right < num.length) {
            for (int i = 0; i < size; i++) {
                temp.add(num[left]);
                left++;
                right++;
            }
            Optional<Integer> max = temp.stream().max(Integer::compareTo);
            max.ifPresent(ret::add);
            left = left - size + 1;
            right = right - size + 1;
            temp = new ArrayList<>();
        }
        return ret;
    }


发表于 2023-10-09 11:07:32 回复(0)
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param num int整型一维数组 
     * @param size int整型 
     * @return int整型ArrayList
     */
    public ArrayList<Integer> maxInWindows (int[] nums, int k) {
        // 1. 参数判断
        if(k <= 0) {
            return new ArrayList<>();
        }
        if(nums.length < k) {
            return new ArrayList<>();
        }
        // 2. 创建滑动窗口最大值的数组
        // 长度为 nums.length - k + 1
        ArrayList<Integer> ans = new ArrayList<>();

        // 3. 定义左右窗口
        int L = 0;// 左窗口
        int R = 0;// 右窗口

        // 4. 创建双端队列来保存区间内的最大值
        LinkedList<Integer> queue = new LinkedList<>();

        // 5. 右窗口往右移动,添加元素
        for(R = 0;R < nums.length;R++) {
            // 6. 如果双端队列没有元素,直接添加
            // 如果双端队列有元素,还要判断队尾的值是否小于等于滑动窗口右侧的值,因为我们要保证双端队列要从大到小排列
            while(!queue.isEmpty() && nums[queue.peekLast()] <= nums[R]) {
                queue.pollLast();
            }

            // 7. 添加元素到双端队列
            // 注意:添加到双端队列的是对应的下标
            queue.addLast(R);

            // 8. 更新双端队列头部的值
            if(queue.peekFirst() == R - k) {
                queue.pollFirst();
            }

            // 9. 添加结果到答案数组中
            // 当 R 来到了第三个位置的时候,就形成了长度为 3 的滑动窗口,此时就可以记录最大值了
            if(R >= k - 1) {
                ans.add(nums[queue.peekFirst()]);
            }
        }

        return ans;
    }
}

发表于 2023-09-10 09:59:46 回复(0)
import java.util.*;

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param num int整型一维数组
     * @param size int整型
     * @return int整型ArrayList
     */
    public ArrayList<Integer> maxInWindows (int[] num, int size) {
        // write code here
        int len = num.length;
        if (size > len || size == 0) {
            return new ArrayList<>();
        }
        int l = 0;
        int r = size;
        int max = Integer.MIN_VALUE;
        for (int i = l; i < r; i++) {
            max = Math.max(max, num[i]);
        }
        ArrayList<Integer> res = new ArrayList<>();
        res.add(max);
        while (r < len) {
            // 移除最大元素,就判断移入的是否最大,如果是就直接更新最大元素,否则循环查找最大元素
            if (num[l] == max) {
                if (num[r] >= max) {
                    max = num[r];
                } else {
                    max = Integer.MIN_VALUE;
                    for (int j = l + 1; j <= r; j++) {
                        max = Math.max(max, num[j]);
                    }
                }
            // 移除的元素不是最大元素,只需要判断移入的是否是大于最大元素,是更新,不是无操作
            } else {
                if (num[r] >= max) {
                    max = num[r];
                }
            }
            res.add(max);
            l++;
            r++;
        }

        return res;
    }
}
发表于 2023-08-14 17:24:02 回复(0)
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param num int整型一维数组
     * @param size int整型
     * @return int整型ArrayList
     */
    public ArrayList<Integer> maxInWindows (int[] num, int size) {
        // write code here
        ArrayList<Integer> res = new ArrayList<Integer>();
        if (size > num.length || size == 0) return res;
        for (int i = 0; i <= num.length - size; i++) {
            Stack<Integer> max = new Stack<Integer>();
            for (int j = i; j  < i + size; j++) {
                int temp = num[j];
                if (max.isEmpty() || (max.peek() < num[j])) {
                    max.push(num[j]);
                } else {
                    max.push(max.peek());
                }
            }
            res.add(max.peek());
        }
        return res;
    }
}

发表于 2023-08-12 16:46:20 回复(0)