239.滑动窗口最大值

239.滑动窗口最大值

题目链接LeetCode-239

题目

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

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

示例:

输入: 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

  1. 使用队列保存最大值。
  2. 依次遍历数组,当前值为nums[i],队尾值为queueLastNumnums[i]queueLastNum比较,当nums[i] > queueLastNum时,queueLastNum被弹出,nums[i]和下一个queueLastNum比较,以此类推,直到nums[i]<=queueLastNum时或队列为空,num[i]直接插入到队列中。
  3. 队列中的值按照在滑动窗口的大小排序,这样保证了,滑动队列一直往后滑动,队头永远为滑动窗口的最大值。
  4. 如果滑动窗口中移除的值nums[i-k+1]为队头,则队列也要移除队头ququeFristNum,说明滑动窗口的最大值被移除了,队列中下一个值变为队头,即为滑动窗口新的最大值。
  5. 注意队列要保存下标而不是值,因为有可能下标不同但是值相同,导致队头被错误移除。

模拟

以上面的数组为例,依次遍历数组,保存最大值的队列为maxQueue,滑动窗口的数量为3

  1. 1进队列(1)
  2. 3 > 1,1被移除,队列为(3)
  3. -1 < 3,-1进入队列(3 -1),滑动窗口为[1 3 -1]
  4. -3 < -1 ,-3队列进入(3 -1 -3),滑动窗口为[3,-1,-3]
  5. 滑动窗口后移,3被移除,队头也为3,3被移除,队列为(-1,-3),5>-3,-3移除,5>-1,-1移除,队列为(5),滑动窗口为[-1 -3 5]
  6. 3 < 5,3进入队列为[5 3],滑动窗口为(-3,5,3)
  7. 6 > 3,3被移除,6 > 5,5被移除,队列为(6),滑动窗口为[5 3 6]
  8. 7 > 6,6被移除,队列为(7),滑动窗口为[3 6 7]
  9. 至此,我们每次循环数组判断,满足滑动窗口的条件,将队头的值作为最大值,保存到结果数组中。当遍历完成,结果数组保存的值也是每个滑动窗口的最大值。

时间复杂度分析

不要以为for里面套了一个while就是O( n 2 n^2 n2),因为队列是动态移除的,并不是固定n个值,极端情况,就是n个值,前n-1个值,都没进入while循环,最后一次,移除队列中所有的值为n,循环了n次。
把n次执行时间均摊到n-1操作,即每次操作都为O(1),时间复杂度即为O(n)。

代码

public int[] maxSlidingWindow(int[] nums, int k) {
    if (nums == null || nums.length == 0) {
        return new int[0];
    }
    
    LinkedList<Integer> maxQueue = new LinkedList<>();
    int[] maxArray = new int[nums.length - k + 1];
    
    for (int i = 0; i < nums.length; i++) {
        // 如果移除的值和maxQueue中保存值相同
        if (maxQueue.peek()!=null && i-k == maxQueue.peekFirst()) {
            maxQueue.pollFirst();
        }
        // 如果队列不为空且当前值大于队尾,依次移除
        while (!maxQueue.isEmpty() && nums[i] > nums[maxQueue.getLast()]) {
            maxQueue.pollLast();
        }
        maxQueue.offer(i);
        if (maxQueue.peekFirst() != null && i >= k-1) {
            maxArray[i - k + 1] = nums[maxQueue.peekFirst()];
        }
    }
    return maxArray;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值