[栈和队列]滑动窗口的最大值

标签:队列,堆,集合
题目描述

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

解题思路
  • 法一:简单的暴力法
  • 法二:双向队列
    用一个双向队列,队列第一个位置保存当前窗口的最大值,当窗口滑动一次,判断当前最大值是否过期(当前最大值的位置是不是在窗口之外),新增加的值从队尾开始比较,把所有比他小的值丢掉。这样时间复杂度为O(n)。
  • 法三:大顶堆(复杂度太高,不建议)
参考代码

双端对列法:不理解的可以看下把每次队列列的size输出的结果:1 1 1 2 1 2 2 2
课件大部分时候队列只有几个元素

   //用双端对列保存的是num中的下标,
   //从双端队列q的左边弹出元素,从右边压入元素(同时删除小的)
   public ArrayList<Integer> maxInWindows(int[] num, int size){
       ArrayList<Integer> res = new ArrayList<>();
       if(num.length == 0 || size > num.length || size <= 0){
           return res;
       }
       Deque<Integer> q = new LinkedList<>();


       //q保存的是num中的下标,从双端队列q的左边弹出元素,从右边压入元素
       //保证存在q中的下标所对应在num里的元素是单调递减的,
       //这样窗口中的最大值一直都是最左边的元素,也就是first元素
       for(int i=0; i<num.length; i++){

           //从左边删除过期元素
           //用来控制窗口的大小始终为size, i是当前下标(这一轮窗口的右边下标), 
           //q的first是窗口的左边下标,保证窗口左边 > 窗口右边 - 窗口大小;
           //队列左边<= i-size 的部分是过期的窗口,需要从队列中 删除
           while(!q.isEmpty() && q.peekFirst() <= i-size){
               q.pollFirst();
           }
           //从队列的右边压入元素, 压入的元素如果大于当前窗口右边的元素,
           //则可以将窗口右边较小的元素删除(此时需要保证这个窗口的元素从左到右是单调递减的)
           while(!q.isEmpty() && num[q.peekLast()]<num[i]){
               q.pollLast();
           }
           q.offerLast(i);     
           //当i >= size-1时,第一个窗口出现,开始输出结果
           if(i >= size - 1)
                res.add(num[q.peekFirst()]);
       }
       return res;
    }

暴力法

import java.util.ArrayList;
public class Solution {
    //暴力,两个循环,外循环记录窗口移动,内循环寻找当前窗口最大值
    //时间O(n^2)
    public ArrayList<Integer> maxInWindows(int [] num, int size)
    { 
        ArrayList<Integer> res = new ArrayList<>(); 
        if(num==null || num.length == 0 || num.length < size || size <= 0)
            return res;
        for(int i=0; i<num.length-size+1; i++){
            int max = num[i];
            for(int j=0 ;j<size; j++){
                if(max < num[i+j])
                    max = num[i+j];
            }
            res.add(max);
        }
     return res;    
    }
   

}

大顶堆法


   //用堆的思想,优先堆列实现,它底层是堆排序的实现,不用自己再建堆和排序调整了
   //复杂度很高,O(n*n*logn)
   public ArrayList<Integer> maxInWindows(int[] num, int size){
       ArrayList<Integer> res = new ArrayList<>();
       if(num.length == 0 || size > num.length || size <= 0){
           return res;
       }
       //因为每次堆顶是保存最大元素,因此是大顶堆
       PriorityQueue<Integer>  maxHeap = new PriorityQueue<>(size,
       (Integer a, Integer b ) -> b - a);
       for(int i=0; i<size; i++){
           maxHeap.offer(num[i]);
       }
       res.add(maxHeap.peek());
       // 定义删除的下标,最早的数组先删
       int flag = 0;
       for(int j=size; j<num.length; j++){
           //先加再删和先删再加
            maxHeap.remove(num[flag++]);
            maxHeap.offer(num[j]);
            res.add(maxHeap.peek());
       }
       return res;  
   }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值