[python 刷题] 239 Sliding Window Maximum
题目:
You are given an array of integers
nums
, there is a sliding window of sizek
which is moving from the very left of the array to the very right. You can only see thek
numbers in the window. Each time the sliding window moves right by one position.Return the max sliding window.
LC 给的第一个案例比较好的解释了这道题什么意思:
Input: nums = [1,3,-1,-3,5,3,6,7], k = 3
Output: [3,3,5,5,6,7]
Explanation:
Window position Max
--------------- -----
[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
我刚开始也用暴力解,结果直接 TLE
后来看了一下别人的题解,发现可以用 queue 去解这道题,解题思路如下:
使用 sliding window 技巧将更新 queue 里面的数据,并且保留以降序的顺序保留 queue 中的值。
当 sliding window 指向 1 时,情况如下:
接下来指针移动到第二个数字,也就是 3,已知 3 > 1 3 > 1 3>1,因此这时候 queue 中比 3 小的值其实都是不需要保留的,这时候情况如下:
然后这时候的区间划到 -1,因为不知道 -1 是不是下个区间最大的数字,因此就需要将 -1 加到 queue 中:
下一步操作同理,因为不知道 -3 是不是下个区间内最大的数次,因此需要将 -3 加入到 queue 中:
接下来遍历到 5,5 比起 queue 中任何一个数字都要大,也就是说对于一直到 5 还是合法区间的区域内,没有保存比 5 小的数字的意义。此时更新 queue:
移动到下个数字 3,同理,这里无法确定 3 是不是会在某个区间内的最大数字,因此需要将其保留在 queue 中:
接下来又到 6,因为 6 比 queue 中所有的数字都要大,因此保留 queue 中的数字没有任何意义,此时 queue 中只有 6
以此类推
知道了具体的实现逻辑,再看一下怎么应用
这道题另一个比较巧妙的点在于,之前碰到的常规题中,直接保存的都是值,而这道题中保存的是下标,通过下标就能够准确的获得 queue 最左侧,也就是 queue 中的最大值应该在什么时候被移除。如:
就知道只有在左侧指针移动到 1 之后,这个数字才会从 queue 中被移除,而左侧指针的判断也很容易,如果数组没有固定长度,可以通过 l e n ( r e s ) len(res) len(res) 判断,或者在当满足 r ≥ k r \ge k r≥k,向 r e s res res 中加值时,修改左侧指针的值
代码如下:
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
q = collections.deque()
l, res = 0, []
for r, ch in enumerate(nums):
# pop smaller value from q
while q and nums[q[-1]] < nums[r]:
q.pop()
q.append(r)
# remove most left value from queue
if l > q[0]:
q.popleft()
if r + 1 >= k:
res.append(nums[q[0]])
l += 1
return res
⚠️:这里使用了 collections.deque()
,原因是因为 collections.deque()
这个数据结构在数组头尾部做增删的时间复杂度都是
O
(
1
)
O(1)
O(1),这样的话就可以有效地将这道题的时间复杂度限制在
O
(
k
×
n
)
O(k \times n)
O(k×n)
使用 priority queue 是写起来最方便的方法,还有另外一个解题思路使用 heap
我一开始也想用这个解题思路的,因为除了构建 heap 之外, heap 的操作都是 l o g ( n ) log(n) log(n),因此这道题的时间复杂度是可以控制在 O ( l o g ( k ) × n ) O(log(k) \times n) O(log(k)×n)。不过 python 原生没有对删除某个固定值的操作,所以这块等到后面复习 heap/priority queue 的时候折腾