队列

队列是一种先进先出(FIFO) 的数据结构,与栈不同,就像排队一样。通常仅允许在队尾查看与添加元素,在队头查看与删除元素。

队列通常由双链表实现,即头指针仅允许在队头查看与删除元素,尾指针在队尾添加元素。

应用场景:当需要按照顺序来处理数据时,可使用队列来解决问题,例如广度优先搜索,或者是🌲树的层级遍历均可以使用队列实现。

双端队列

与队列不同,可同时在队尾与队头添加删除与查看数据。

应用场景:实现一个长度动态变化的窗口或连续区间,动态窗口这种数据结构。

239 滑动窗口的最大值

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

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

来源:力扣(LeetCode)

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

滑动窗口的位置 最大值


[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7

提示:你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。

  1. 暴力解法

    仍然是遍历就完事了,注意空数组,k=0的特殊入参校验就好了。

    class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int[] res = new int[nums.length-k+1];
        if(res.length<1|| k==0){
            return new int[k];
        }
        for (int i = 0; i +k-1<nums.length; i++) {
            int max = nums[i];
            for (int j = i; j <i+k ; j++) {
                if (nums[j]>max){
                    max = nums[j];
                }
            }
            res[i] = max;
        }
        return res;
    }
    
  2. 使用双端队列
    看了leetcode的官方题解与一个算法课中的题解都不是很理解,最后扒出来一个好理解的题解(https://leetcode-cn.com/problems/sliding-window-maximum/solution/shuang-xiang-dui-lie-jie-jue-hua-dong-chuang-kou-2/),然后自己撸了一遍才明白, 但还是不太明白是如何想到的使用双端队列这种数据结构。

    对我而言这个题的难点在于哪一步为结果数组赋值,以及如何维护该队列,还有就是边界值的判断。思路如下:

    我们使用一个双端队列保存当前滑动窗口中的最大值的下标,并保证队头始终是当前滑动窗口的最大值下标,凡事当前元素大于队尾元素的,队尾均出队;因此,该队列从队头到队尾始终是从大到小的,我们在每次获得结果时,只需要取队头元素即可;

    另外需要注意就是,要判断队头元素是否还在当前滑动窗口,如果不在应及时出队,使用L,R分别表示当前滑动窗口的左右两端,其中i为R,i-k 为L ;判断条件即 deque.peekFirst() <= i - k

    另外在刚开始遍历数组时,滑动窗口不能达到k的长度,因此也获取不结果,也应做边界判断,即i-k+1>=0

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || nums.length < 2) {
            return nums;
        }
        int[] res = new int[nums.length - k + 1];
        Deque deque = new LinkedList();
        for (int i = 0; i < nums.length; i++) {
            //确保队列从队头到队尾从大到小
            while (!deque.isEmpty() && nums[(int) deque.peekLast()] <= nums[i]) {
                deque.pollLast();
            }
            deque.addLast(i);
            //判断队头元素是否还在窗口中
            if ((int) deque.peekFirst() <= i - k) {
                deque.pollFirst();
            }
            //判断窗口长度是否达到k
            if (i - k + 1 >= 0) {
                res[i - k + 1] = nums[(int)deque.peekFirst()] ;
            }
        }
        return res;
    }
}

Waters
7 声望0 粉丝

Keep peace and carry on.