数据结构与算法随笔之优先队列-求滑动窗口最大值(三)

lickylin 2019-06-28

这篇文章我们来看一道题目求滑动窗口最大值问题(在leetcode上的地址:滑动窗口最大值)

题目描述

给定一个长度为N的数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口 k 内的数字。滑动窗口每次只向右移动一位。返回滑动窗口最大值。

示例:

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]

解决方案

一、使用最大堆来实现

首先定义一个大小为K的最大堆,把窗口里面的数据入堆,这样堆顶的数据就是最大值,当窗口向右移动的时候,我们还需要做的一件事情就是把不在窗口的数据移出堆,把进入窗口的数据放入堆,此时堆也会做相应调整,维持最大值在堆顶。代码如下:

public class SlidingWindow {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums == null || nums.length == 0 || k < 1 || k > nums.length) {
            return null;
        }
        if(k == 1) {
            return nums;
        }
        int[] result = new int[nums.length - k + 1];
        int j = 0;
        //用优先队列构建最大堆
        PriorityQueue<Integer> queue = new PriorityQueue<>(k, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                if(o1.compareTo(o2) == 0) {
                    return 0;
                }else if(o1.compareTo(o2) > 0) {
                    return -1;
                }else {
                    return 1;
                }
            }
        });
        for(int i=0;i<nums.length;i++) {
            //把不在窗口的数据移除掉
            if(i-k+1 > 0) {
                queue.remove(nums[i-k]);
            }
            //把移进窗口的数据加入最大堆,最大值一定会在堆顶
            queue.add(nums[i]);
            if(i-k+1 < 0) {
                continue;
            }
            result[j++] = queue.peek();
        }
        return result;
    }
}

复杂度分析

  • 时间复杂度:O(nlogk)

二、使用双端队列来实现

定义一个大小为K的双端队列,把窗口里的数据放入队列,每次入队的时候进行判断,队列里面小于入队数据时,需要出队,一定保持最大值在队列的最左端,代码实现如下:

public class SlidingWindow {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums == null || nums.length == 0 || k < 1 || k > nums.length) {
            return null;
        }
        if(k == 1) {
            return nums;
        }
        int[] result = new int[nums.length - k + 1];
        int m = 0;
        ArrayDeque<Integer> queue = new ArrayDeque<>(k);
        for(int i=0;i<nums.length;i++) {
            //把不在窗口的数据移除掉
            while(!queue.isEmpty() && queue.peek() < i-k+1) {
                queue.remove();
            }
            //比较大小,把较小的数据移除掉,保证最大值在队列的最左端
            while(!queue.isEmpty() && nums[queue.peekLast()] <= nums[i]) {
                queue.removeLast();
            }
            queue.add(i);
            if(i-k+1 < 0) {
                continue;
            }
            result[m++] = nums[queue.peek()];
        }
        return result;
    }
}

复杂度分析

  • 时间复杂度:O(n)

相关推荐