剑指offer64 滑动窗口的最大值


剑指offer64

滑动窗口的最大值

题目描述

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

示例一

输入

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

输出

[4,4,6,6,6,5]

思路

滑动窗口应当是队列,但为了得到滑动窗口的最大值,队列序可以从两端删除元素,因此使用双端队列。

原则: 对新来的元素k,将其与双端队列中的元素相比较

1)前面比k小的,直接移出队列(因为不再可能成为后面滑动窗口的最大值了!),

2)前面比k大的X,比较两者下标,判断X是否已不在窗口之内,不在了,直接移出队列

队列的第一个元素是滑动窗口中的最大值;

特别说明,我们在双端队列中保存的数字是传入的向量的下标!

代码

import java.util.*;
public class Solution {

    public ArrayList<Integer> maxInWindows(int [] num, int size)
    {
        ArrayList<Integer> ret = new ArrayList<>();
        if (num == null || num.length < size || size < 1) {
            return ret;        //处理特殊情况
        }

        LinkedList<Integer> indexDeque = new LinkedList<>();
        //处理前size个数据,因为这个时候不需要输出最大值
        for (int i = 0; i < size - 1; i++) {
            //假如当前的元素比队列队尾的元素大,说明之前加入的这些元素不可能是最大值了。直接移除
            while (!indexDeque.isEmpty() && num[i] > num[indexDeque.getLast()]) {
                indexDeque.removeLast();//弹出比当前小的元素下标
            }
            indexDeque.addLast(i);//队尾压入当前下标
        }
        //处理size往后的元素,这时候需要输出滑动窗口的最大值
        for (int i = size - 1; i < num.length; i++) {
            while (!indexDeque.isEmpty() && num[i] > num[indexDeque.getLast()]) {
                indexDeque.removeLast();    //从后面依次弹出队列中比当前num值小的元素,同时也能保证队列首元素为当前窗口最大值下标
            }
            indexDeque.addLast(i);//将当前下标压入队尾,因为可能在未来是最大值
            if (i - indexDeque.getFirst() + 1 > size) {//判断队头的下标是否超出size大小,如果超过,要删除队头元素
                indexDeque.removeFirst();    //当当前窗口移出队首元素所在的位置,即队首元素下标对应的num不在窗口中,需要弹出
            }
            ret.add(num[indexDeque.getFirst()]);//最后将队首元素添加进结果数组
        }
        return ret;
    }
}
/**
ArrayDeque不是线程安全的。
ArrayDeque不可以存取null元素,因为系统根据某个位置是否为null来判断元素的存在。
当作为栈使用时,性能比Stack好;当作为队列使用时,性能比LinkedList好。
*/

此外,可以使用双端队列 ArrayDeque 类实现:

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;
    }
}

附:ArrayDeque类

参考 https://blog.csdn.net/skh2015java/article/details/74840513


文章作者: Hailong Gao
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Hailong Gao !
评论
  目录