今天终于来到了第17题,也是第一道困难题目啦(偷偷吐槽句,题目给的真多…)
T239 滑动窗口最大值
给你一个整数数组 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
解题思路:
如果不考虑时间复杂度问题,这道题应该不算什么难题,对数组进行依次遍历即可。考虑时间复杂度的情况下,我们使用堆来解决。
取nums中前k个元素放入堆中,以[-nums[i], i]的形式进行储存;负号的意义在于使堆按大堆进行排序,而i为该元素的索引。依次取k+1到nums末尾的元素,将元素放入堆中重新排序,取出堆中的最大值放入res中。倘若滑动窗口已经划过堆中的最大元素,则将最大元素从堆中pop,直至找到堆中在滑动窗口内的最大元素,代码如下:
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
tr = [[-nums[i], i] for i in range(k)]
heapq.heapify(tr)
res = [-tr[0][0]]
for i in range(k,len(nums)):
heapq.heappush(tr, [-nums[i],i])
while tr[0][1] <= i-k:
heapq.heappop(tr)
res.append(-tr[0][0])
return res
运行结果:
这里再提出一种相较于暴力求len(nums)-k次更优的一种算法(虽然没过的了测试…)
取出前k个数的最大值后,将窗口依次向右滑动,若新进入的数比res中最后一个数大,则将新进入的nums[i]加入res,否则仍加入res[-1]。窗口没向右一格后将最左端元素pop,若pop元素为窗口中最大元素,则再取一次窗口中的最大值。这种算法避免了每次取k的值进行比较,只需比较新进入的数与原本窗口中最大的元素即可,减少了时间的复杂程度,代码如下:
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
if k==1:
return nums
res = [max(nums[:k])]
test = nums.copy()
i = k
for num in test:
if len(nums) == k:
break
if nums.pop(0) != res[-1]:
res.append(max(res[-1],test[i]))
else:
res.append(max(nums[:k]))
i += 1
return res