【Leetcode】239. 滑动窗口最大值

145 阅读1分钟

题目描述

在这里插入图片描述

// 239. 滑动窗口最大值

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

// 返回滑动窗口中的最大值。

题解

// 最大堆法
// 本题和【剑指offer】59. 滑动窗口的最大值 一模一样
// 最大堆法理论上可行,但会超出时间限制。
import java.util.PriorityQueue;
class Solution {
    public int[] maxSlidingWindow(int[] nums, int size) {
		ArrayList<Integer> resList = new ArrayList<>();
		PriorityQueue<Integer> maxHeap = new PriorityQueue<>(new Comparator<Integer>(){
			@Override
			public int compare(Integer o1, Integer o2) {
				return o2.compareTo(o1);
			}
		});
		for (int i = 0; i < size; i++) {
			maxHeap.offer(nums[i]);
		}
		resList.add(maxHeap.peek());
		
		for (int l = 0, r = size + l; r < nums.length; l++, r++) {
			maxHeap.add(nums[r]);
			maxHeap.remove(nums[l]);
            resList.add(maxHeap.peek());
		}
		
		return arrayToList(resList);
    }
	
	public int[] arrayToList(ArrayList<Integer> resList) {
		int[] res = new int[resList.size()];
		for (int i = 0; i < resList.size(); i++) {
			res[i] = resList.get(i);
		}
		return res;
	}
}


// 简化一下
// 还是通不过,看来这个方法真的太慢,不太行
class Solution {
    public int[] maxSlidingWindow(int[] nums, int size) {
		int[] res = new int[nums.length - size + 1];
		PriorityQueue<Integer> maxHeap = new PriorityQueue<>(new Comparator<Integer>(){
			@Override
			public int compare(Integer o1, Integer o2) {
				return o2.compareTo(o1);
			}
		});
		for (int i = 0; i < size; i++) {
			maxHeap.offer(nums[i]);
		}
		res[0] = maxHeap.peek();
		for (int l = 0, r = size + l; r < nums.length; l++, r++) {
			maxHeap.add(nums[r]);
			maxHeap.remove(nums[l]);
            res[r - size + 1] = maxHeap.peek();
		}
		
		return res;
    }
}
// 双端队列
// 本解法和【剑指offer】59. 滑动窗口的最大值 一样。
// 双端队列法旨在用双端队列deque来维护窗口元素特别是窗口的最大值元素。
// 
// 先排除特殊情况,如果size比nums长度还大,或者size比0小,直接返回空数组。
// 
// 定义双端队列deque(用链表代替),定义相应大小的答案保存数组res,
// 第一个for循环将nums中的前size个元素nums[i]从尾部存入deque。由于我们不需要
// deque窗口元素中的所有值,而是只要窗口元素中的最大值,所以如果deque不
// 为空且将要存入的nums[i],比deque的尾部元素要大,在nums[i]存入deque之前,
// 我们将deque尾部元素循环移除,直到deque尾部元素大于等于nums[i],
// 或者直到deque元素已经排空了,这时候再将nums[i]存入,这样子deque的头部
// 元素一定是deque中的最大值元素,并且deque头部到尾部的元素呈现降序的排序。
// 第一个for循环起初始化窗口的作用,将nums中前size个元素放入deque并规范成
// 降序链表,我们可以轻松从头部取到窗口最大值。
// 
// 定义res遍历索引j,初始化为0,如果deque初始化好了(非空),将deque的
// 头部元素(窗口最大值)存入res[0]中,j右移。
// 
// 第二个for循环,从nums的size索引开始遍历nums的元素nums[i],则nums[i]
// 为窗口的右索引元素,则窗口的左索引元素为nums[i - size],在循环内,
// 条件判断:如果deque的头部元素(窗口最大值)等于左索引元素,则将
// deque中的头部元素去除。
// 然后调用while循环,与第一个for循环初始化deque时相似,如果deque不为空
// 且将要存入的nums[i],比deque的尾部元素要大,在nums[i]存入deque之前,
// 我们将deque尾部元素循环移除,直到deque尾部元素大于等于nums[i],
// 或者直到deque元素已经排空了,这时候再将nums[i]存入。窗口完成一格移动,
// 将res[j]位置赋值为deque的头部元素(窗口最大值),j右移。
// 第二个for循环,主要是完成窗口移动的过程,通过双端链表和插入元素前的
// 贪心删除元素,来维护一个降序的双端链表,所以头部元素就是窗口最大值。
// 
// 如此循环,最后返回res即可。
// 
// 执行用时:35 ms, 在所有 Java 提交中击败了82.67%的用户
// 内存消耗:52.1 MB, 在所有 Java 提交中击败了66.37%的用户
class Solution {
    public int[] maxSlidingWindow(int[] nums, int size) {
		if (size > nums.length || size < 1)
			return new int[0];
		Deque<Integer> deque = new LinkedList<>();
		int[] res = new int[nums.length - size + 1];
		for (int i = 0; i < size; i++) {
			while (!deque.isEmpty() && deque.peekLast() < nums[i]) {
				deque.removeLast();
			}
			deque.addLast(nums[i]);
		}
        int j = 0;
		if (!deque.isEmpty())
			res[j++] = deque.peekFirst();
		for (int i = size; i < nums.length; i++) {
			if (!deque.isEmpty() && deque.peekFirst() == nums[i - size]) {
				deque.removeFirst();
			}
			while (!deque.isEmpty() && deque.peekLast() < nums[i]) {
				deque.removeLast();
			}
			deque.addLast(nums[i]);
			res[j++] = deque.peekFirst();
		}
		return res;
    }
}