【面试题】滑动窗口的最大值(两种解法+Java、Python双语言实现)

题目描述

在这里插入图片描述

解法一:暴力法

最简单直接的方法是遍历每个滑动窗口,找到每个窗口的最大值。假设数组的长度为n,则一共有 n - k + 1 个滑动窗口,每个滑窗有 k 个元素,遍历 n - k + 1次,每次从 k个数中寻找最大值,于是算法的时间复杂度为 O(n*k),表现较差。

Java

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums.length == 0 || nums.length < k || k == 0) return new int[0];
        int[] res = new int[nums.length - k + 1];
        for(int i=0; i<nums.length - k + 1; i++) {
            res[i] = Arrays.stream(nums, i, i+k).max().getAsInt(); // 获取当前子数组的最大值       
        }
        return res;
    }
}

Python

class Solution(object):
    def maxSlidingWindow(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        res = []
        for i in range(len(nums)-k+1):
            res.append(max(nums[i:i+k]))
        return res

解法二:设计单调双端队列

题解思路参考
在这里插入图片描述

Java

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums.length == 0 || k == 0) return new int[0];
        Deque<Integer> deque = new LinkedList<> ();
        int[] res = new int[nums.length-k+1];
        for(int i=0; i<k; i++) { // 未形成窗口
            // 每次添加新元素前,删除当前 deque内所有小于该元素的值,以保持 deque非严格递减,队首始终存放队列中最大元素,
            // 因为比新加入元素更小的元素不可能会成为窗口最大值,直接剔除即可。
            while(!deque.isEmpty() && deque.peekLast()<nums[i]) 
                deque.removeLast();         
            deque.addLast(nums[i]); // 加入新元素
        }
        res[0] = deque.peekFirst(); // 当形成首个窗口后,此时队首即为窗口最大值
        
        for(int j=k; j<nums.length; j++) { // 窗口滑动
            if(deque.peekFirst() == nums[j-k]) // 每轮窗口滑动会移除元素nums[j-k],需要将deque中对应元素一起删除
                deque.removeFirst(); // 若此时队首元素恰好是此轮为移出窗口的元素,则队首元素出队。
            while(!deque.isEmpty() && deque.peekLast()<nums[j]) // 保持队首始终存放的是队列中最大元素
                deque.removeLast();
            deque.addLast(nums[j]);
            res[j-k+1] = deque.peekFirst(); // 队首即为此轮窗口内最大值
        }
        return res;
    }
}

另一种写法,集成到一个 for循环中

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums.length == 0 || nums.length < k || k == 0) return new int[0];
        Deque<Integer> deque = new LinkedList<> ();
        int[] res = new int[nums.length - k + 1];
        for(int i=0; i<nums.length; i++) {
            // 当遍历过的元素超过 k 个,即产生首个窗口后,才开始判断队列中是否存在已经滑出窗口的对应元素,有则剔除
            if(i>=k && deque.peekFirst() == nums[i-k]) deque.removeFirst(); 
            // 始终保持队首存放当前窗口内最大元素,注意不能写成 <= nums[i],因为有可能存在多个重复的值均为当前最大值,
            // 后面删除时,也是先删除下标靠前的,后面重复的元素保留
            while(!deque.isEmpty() && deque.peekLast() < nums[i]) 
                deque.removeLast();
            deque.addLast(nums[i]); // 当前窗口右边界滑过的新元素入队
            if(i>=k-1) res[i-k+1] = deque.peekFirst(); // 当滑窗滑过的元素超过 k个时,才开始判断第一个窗口最大值,每轮窗口最大值即为当前队首元素
        }
        return res;
    }
}

Python

class Solution(object):
    def maxSlidingWindow(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        #----采用双端队列-----
        win = [] # 滑动窗口,注意:里头保存的是索引值
        res = []
        for i, v in enumerate(nums):
            if i >= k and win[0] == i-k: # 当有元素从滑窗左边界滑出的时候,如果它恰好是窗口内的最大值(位于队首 win[0]),将它弹出
                win.pop(0)
            while win and nums[win[-1]] <= v: # 如果滑动窗口非空,新进来的数比队列里某些已经存在的数还要大时
                win.pop() # 则说明这些已经存在的数不可能成为滑动窗口的最大值(它们永无出头之日),将它们全部弹出
            win.append(i) # 将每个元素的索引存放进 win中
            if i >= k-1: # 当滑窗滑过的元素超过 k个时,才开始判断第一个最大值
                res.append(nums[win[0]]) # 队首是滑动窗口内最大值的索引
        return res

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值