双端队列之滑动窗口最大值

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

暴力法:

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        n = len(nums)
        if n <= k:
            return [max(nums)]
        res = []
        left = 0
        while left < n - k + 1:
            res.append(max(nums[left:left+k]))
            left += 1
        return res

复杂度分析

时间复杂度:O(Nk)。其中 N 为数组中元素个数。
空间复杂度:O(N−k+1),用于输出数组。

双端队列存索引值
如果“当前考虑的数”比之前来的数还要大,那么之前的数(如果还没有划出“滑动窗口”)就一定不会是“滑动窗口”的最大值,应该把它们移除,因为它们“永远不会有出头之日”。
“左边界出滑动窗口的时候”只有 1 种情况需要考虑,那就是:
左边界恰好就是上一轮循环的滑动窗口的最大值,在这一轮“滑动窗口”右移,它必须被移出。

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        n = len(nums)
        if n == 0:
            return []
        res =[]
        window = deque() # 双端队列,存放索引值
        # 或者使用 window = []
        for i in range(n):
            # 当元素从左边界滑出的时候,如果它恰好是滑动窗口的最大值,那么将它弹出
            if i >= k and i - k == window[0]:
                window.popleft()
                # window.pop(0)
                
            # 如果滑动窗口非空,新进来的数比队列里已经存在的数还要大,则说明队列里已经存在的数一定不是滑动窗口最大值,将他们弹出
            while window and nums[window[-1]] < nums[i]:
                window.pop()
                
            window.append(i)
            
            # 队首一定是滑动窗口的最大值的索引
            if i >= k - 1:
                res.append(nums[window[0]])
                
        return res

补充:python双端队列
Python的deque模块,它是collections库的一部分。deque实现了双端队列,意味着你可以从队列的两端加入和删除元素。

append(x):把元素x添加到队列的右端
appendleft(x):把元素x添加到队列的左端
clear():清空队列中所有元素
copy():创建队列的浅拷贝
count(x):计算队列中等于x元素的个数
extend(iterable):在队列右端通过添加元素扩展
extendleft(iterable):在队列左端通过添加元素扩展
index(x[, start[, stop]]):返回x元素在队列中的索引,放回第一个匹配,如果没有找到抛ValueError
insert(i, x):在队列的i索引处,插入x元素
pop():移除并返回deque右端的元素,如果没有元素抛IndexError
popleft():移除并返回deque左端的元素,如果没有元素抛IndexError
remove(value):删除第一个匹配value的元素,如果没有找到抛ValueError
reverse():在原地反转队列中的元素
rotate(n):把队列左端n个元素放到右端,如果为负值,右端到左端。如果n为1,等同d.appendleft(d.pop())
maxlen:只读属性,队列中的最大元素数

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值