LeetCode239. 滑动窗口最大值,单调栈

1. 题目描述

题目来源:力扣

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

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

示例1. 
输入: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
示例2. 
输入:nums = [1], k = 1
输出:[1]

2. 题解

2.1 题解思路

方法1,暴力解法

每一次滑动窗存储k个元素,每次调用max对k个值求最大值,因此算法的复杂度为O(n*k),代码如下,

class Solution(object):
    def maxSlidingWindow(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        stack = []
        r = []
        if len(nums) <= k:
            r.append(max(nums))
            return r

        for i in range(k):
            stack.append(nums[i])
        r.append(max(stack))
        for i in range(k, len(nums)):
            stack.pop(0)
            stack.append(nums[i])
            r.append(max(stack))

        return r

方法2,单调队列

栈的特点:后入先出,先入后出;和队列正好相反,队列先入先出。栈和对列的示意图如下,

图1. 栈和队列

 单调队列是指,队列内的元素按照从小到大或者从大到小的顺序排列。对于一个单调递减的单调队列,出口元素(第一个元素)为最大的元素,那么在遍历n个元素的过程中,首先将第一个元素压入队列内,下面:

pop(出口元素):若此时的移动窗口已滑过当前的值,那么pop出口元素;

insert(入口元素):若是当前val大于入口元素,那么,pop入口元素,直到val<=入口元素为止。当前val入队列。(保证了队列是单调的,且在窗口内可能会成为最大值)

因此,在改题目中,单调栈内维护的元素为在窗口大小k内可能成为最大值的元素,且包含重复值(防止当前元素为最大,但是由于上一个元素相等且已移出队列。)

代码如下,

class Solution(object):
    def pop(self, queue, ele):
        if queue != [] and queue[0] == ele:
            queue.pop(0)
        return queue
    def push(self, queue, ele):
        while queue != [] and ele > queue[-1]:
            queue.pop(-1)
        queue.append(ele)
        return queue

    def maxSlidingWindow(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        r = []
        queue = []
        for i in range(k):
            queue = self.push(queue, nums[i])
        r.append(queue[0])

        for i in range(k, len(nums)):
            queue = self.pop(queue, nums[i-k])
            queue = self.push(queue, nums[i])
            r.append(queue[0])
        return r

在上面的解法中,运行测试时间为9320ms,换用动态的from array import array使用动态的数组,将

r = []

queue = []

换为

r = array('h', [])

queue = array('h', [])

其中h代表signed short int,代码如下,

from array import array
class Solution(object):
    def pop(self, queue, ele):
        if len(queue) != 0 and queue[0] == ele:
            queue.pop(0)
        return queue
    def push(self, queue, ele):
        while len(queue) != 0 and ele > queue[-1]:
            queue.pop(-1)
        queue.append(ele)
        return queue

    def maxSlidingWindow(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        # r = array('l', [])
        # queue = array('l', [])
        r = array('h', [])
        queue = array('h', [])
        for i in range(k):
            queue = self.push(queue, nums[i])
        r.append(queue[0])

        for i in range(k, len(nums)):
            queue = self.pop(queue, nums[i-k])
            queue = self.push(queue, nums[i])
            r.append(queue[0])
        return list(r)

运行时间缩短为2852ms。因此可见,动态数组在pop元素时效率比静态数组高。

关于from array import array,用法见python中 array模块学习笔记_阿常呓语的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值