剑指Offer 59.队列的最大值(Python)

滑动窗口的最大值

给定一个数组和滑动窗口的大小,请找出所有滑动窗口里的最大值。
样例:

输入:数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3
输入:一共存在6个滑动窗口,它们的最大值分别为{4,4,6,6,6,5}

解题思路

蛮力法

扫描每个滑动窗口的所有数字并找出其中的最大值。如果滑动窗口为k,则需要 O ( k ) O(k) O(k)时间才能找出滑动窗口里的最大值。对于长度为n的输入数组,这种算法的总时间复杂度是 O ( n k ) O(nk) O(nk).


双向队列

定义一个双向队列来存储滑窗的最大值,所谓双向队列,就是同时具有队列和栈的性质的数据结构。在Python中,再不调用其他模块的情况下,也可以用list来实现。
结合例子来进行说明:

输入:数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3
第一步插入首个元素2,此时滑窗为[2],我们将元素的下标存进双向队列[0](为什么是下标等会再说);
第二步插入元素3,因为3比2大,2不可能是滑窗的最大值,因此弹出2的下标,此时滑窗为[2,3],队列为[1];
第三步插入元素4,同理4比3大,弹出3,此时滑窗为[2,3,4](已经达到滑窗本身k的大小),队列为[2];
第四步插入元素2,2虽然比4小,但4滑出窗口后,2是有可能成为最大值的,先插入队列,此时滑窗为[3,4,2],队列为[2,3];
第五步插入元素6, 6比2,4都大,因此都弹出,此时滑窗为[4,2,6],队列为[4];
第六步插入元素2, 2虽然比6小,但先考虑保留,此时滑窗为[2,6,2],队列为[4,5];
第七步插入元素5, 5也比6小,考虑保留,此时滑窗为[6,2,5],队列为[4,5,6];
第八步插入元素1, 这里要注意6已经移出滑窗了,因此我们要先将6的下标4弹出,此时滑窗为[2,5,1],队列为[6,7].

为什么我们保存下标,而不是元素本身?
因为还要考虑移出滑窗的情况,必须保存元素的下标,才能判断元素是不是移出滑窗了。

关于滑窗的最大值
因为我们是从数组的开头开始遍历,因此要注意,到第k-1个元素开始才会有滑窗的最大值
比如上例中,滑窗的大小为3,就要遍历到nums[2],才会有最大值;
滑窗的最大值都位于双向队列的首位。

算法的时间复杂度为 O ( n ) O(n) O(n).

class Solution(object):
    def maxInWindows(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        if not nums or k <= 0: return None
        if k == 1: return nums
        
        deque = [0]
        res = []
        for i in range(1,len(nums)):
            while deque and nums[deque [-1]] <= nums[i]:
                deque .pop(-1)
            if deque and deque [0] <= i - k:
                deque .pop(0)
            deque .append(i)
            if i >= k - 1:
                res.append(nums[deque [0]])
        return res

你可能感兴趣的:(剑指Offer)