@liweiwei1419
2019-05-24T05:29:04.000000Z
字数 1532
阅读 1649
滑动窗口 索引数组
传送门:滑动窗口最大值。
要求:给定一个数组 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 31 [3 -1 -3] 5 3 6 7 31 3 [-1 -3 5] 3 6 7 51 3 -1 [-3 5 3] 6 7 51 3 -1 -3 [5 3 6] 7 61 3 -1 -3 5 [3 6 7] 7注意:
你可以假设 k 总是有效的,1 ≤ k ≤ 输入数组的大小,且输入数组不为空。
进阶:
你能在线性时间复杂度内解决此题吗?
分析:

关键:
1、如果后进来一个数,前面的元素比它小,那么前面的元素就永远不可能是“滑动窗口中的最大值”;
2、如果判断当前“滑动窗口中的最大值”应该被移除掉,所以“滑动窗口”中应该保存的是下标。
直接给出最佳解法:

思考:为什么要存的是下标,因为要判断当前“滑动窗口”的最大值是否被滑出边界。
# 同 LeetCode 第 239 题,传送门:滑动窗口最大值。class Solution:def maxSlidingWindow(self, nums, k):""":type nums: List[int]:type k: int:rtype: List[int]"""# 关键:如果后进来一个数,前面的元素比它小# 那么前面的元素就永远不可能是"滑动窗口中的最大值"l = len(nums)if l == 0 or k <= 0:return []res = []window = []for i in range(l):# 考虑什么时候,要把最大移除# 左边界划出的时候,应该是 window.pop(0)# [0,1,2,3,4]# [ i]# window[0] == i - k 这个条件特别容易忽略# 这一步条件特别重要:只有当当前最大元素刚刚滑出“滑动窗口”时,才可以把 window[0] 去掉if i >= k and window[0] == i - k:window.pop(0)# 考虑把不可能是最大的元素全部 kill 掉while window and nums[i] >= nums[window[-1]]:window.pop()window.append(i)# 什么时候有滑动窗口呢?if i >= k - 1:res.append(nums[window[0]])return resif __name__ == '__main__':nums = [1, 3, -1, -3, 5, 3, 6, 7]k = 3solution = Solution()result = solution.maxSlidingWindow(nums, k)print(result)
总结:
1、整个过程中,保持 windows[0] 对应的下标就是滑动窗口的最大值。
覃超给的解法:
1、使用大顶堆,但是要使用索引堆。维护这个堆的元素固定,维护堆顶元素是堆中最大的,时间复杂度:;
2、使用双端队列:时间复杂度 。下面这个版本的写法是最优解:多写几遍就能理解。说明:window 中存储的是下标。

说明:window[0] <= i-k 应该是 i-k ,表示,此时滑动窗口中最大的那个元素正好滑出“滑动窗口”了。
具体例子:
| 数值 | 1 | 3 | -1 | -3 | 5 | 3 | 6 |
|---|---|---|---|---|---|---|---|
| 索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
如果遍历的指针 i 在 那个位置上,滑动窗口的长度是 ,此时索引 就应该是第 1 个滑出“滑动窗口”的元素。
其它解法:两个 for 循环,第一个 for 循环滑动窗口,第二个 for 循环滑动窗口中的值,寻找最大值。还可以使用时间复杂度更低的双端队列求解。
Java 写法:
(本节完)