【数据结构与算法】滑动窗口

143 阅读2分钟

这是我参与8月更文挑战的第29天,活动详情查看:8月更文挑战

滑动窗口

可以理解为一个可以容纳n个元素的窗口,通过每次记录窗口的状态,找到符合条件的窗口或者说在窗口中得到符合条件的元素。 通过滑动窗口可以减少时间复杂度

【题目】

有一个整型数组arr和一个大小为w的窗口从数组的最左边滑到最右边,窗口每次 向右边滑一个位置。 例如, 数组为[4,3,5,4,3,3,6,7], 窗口大小为3时: image.png

如果数组长度为n, 窗口大小为w, 则一共产生n-w+1个窗口的最大值。
请实现一个函数。 输入:整型数组arr, 窗口大小为w。
输出:一个长度为n-w+1的数组res, res[i]表示每一种窗口状态下的 以本题为例, 结果应该返回{5,5,5,4,6,7}。

【代码实现】

import java.util.Arrays;
import java.util.LinkedList;

public class SildingWindow {
    public static void main(String[] args) {
        int[] arr={4,3,5,4,3,3,6,7};
        int[] res = slidingWindow(arr, 3);
        System.out.println(Arrays.toString(res));
    }

    /*如果数组长度为n, 窗口大小为w, 则一共产生n-w+1个窗口的最大值。
请实现一个函数。 输入:整型数组arr, 窗口大小为w。
输出:一个长度为n-w+1的数组res, res[i]表示每一种窗口状态下的 以本题为例, 结果应该
返回{5,5,5,4,6,7}。*/
    public static int[] slidingWindow(int[] arr, int w) {
        //1.定义一个双端队列,存放的是arr数组的下标
        LinkedList<Integer> deque = new LinkedList<>();
        //2.定义一个数组用来保存窗口最大值
        int[] res = new int[arr.length - w +1];
        int index=0;  //辅助变量给res使用
        for (int i = 0; i < arr.length; i++) {  //3.从左到右移动窗口

            while (!deque.isEmpty() && arr[deque.peekLast()] <= arr[i]) { // 4.判断添加的时候是否需要将deque中last元素弹出
                //当deque中最后一个值对应的arr的值小于或者等于ar[i]的时候,要将deque最后的值弹出直到arr[last]的值大于arr[i]或者deque为空
                deque.pollLast();
            }
            deque.addLast(i);  //将当前位置的索引加入到队列中
            if(deque.peekFirst()==i-w){
                deque.pollFirst();
            }
            if (i >=w - 1) {  //由于i要至少到大w-1位置这个时候窗口才是满的 刚开始窗口中的数的个数为1,2...w
                res[index++]=arr[deque.peekFirst()];    //5.将窗口最大值加入数组
            }
        }
        return res;
    }
}

【总结】

  • 每个位置的数最多进窗口一次出窗口一次,弹出过的数不会找回
  • 如果滑过的数字为n,双端队列更新的总代价时间复杂度O(N)
  • 双端队列滑过n次,双端队列更新的平均代价时间复杂度O(1)