剑指64.滑动窗口的最大值

题目描述

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。
窗口大于数组长度的时候,返回空
 

思路

一个滑动窗口可以看成一个队列,当窗口滑动时,处于窗口的第一个数字被删除,同时在窗口的末尾添加一个新的数字,这符合队列“先进先出”。

思路1:暴力法。时间复杂度为O(nk)

思路2:增减维护一个优先队列。时间复杂度O(n logk)

 

思路3:通过单调Deque双端队列。Deque里头最大,尾最小。注意Deque里存的是index,不是值。                          (时间复杂度O(n),每个下标最多进一次,出一次。)

一共有n-(size-1)个滑动窗口

注意要分清队列的头和尾,“头在左,尾在右”。因为,窗口向右移动,队列是尾入头出。

步骤:头尾尾头

  • 1)头部出队
  • 2)尾部清理(因为要删除尾部,所以要用双端队列)
  • 3)尾部入队
  • 4)返回头部(从左往右看)

 

☆☆☆☆解法

import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
public class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size) {
        ArrayList<Integer> list = new ArrayList<>();
        if (num == null || size < 1 || num.length < size)
            return list;
        // 双端队列,deque里存的是数组的index,不是数组的值
        Deque<Integer> deque = new LinkedList<>();
        for (int i = 0; i < num.length; i++) {
            //Step1: 头: 移除头部, 保证窗口的长度范围
            if (!deque.isEmpty() && (i - deque.getFirst()) >= size){ //这个条件也可以换成 deque.getFirst() < (i - (size - 1))
                deque.removeFirst();
            }
            //Step2: 尾: 移除尾部小于当前值的元素, 去除不可能的元素
            while (!deque.isEmpty() && num[i] >= num[deque.getLast()]){
                deque.removeLast();
            }
            //Step3: 尾部加入, 滑动窗口向右扩充
            deque.addLast(i);
            //Step4: 头, 从头部返回极大值
            if (i >= size - 1){
                list.add(num[deque.getFirst()]);
                //list.add(num[deque.peekFirst()]); //所有的dq.getFirst()也可以换为dq.peekFirst()
            }
        }
        return list;
    }
}

 

 

参考:

易懂视频 java版本 双端队列的讲解

 

posted @ 2020-09-11 11:36  不学无墅_NKer  阅读(156)  评论(0编辑  收藏  举报