打败算法 —— 滑动窗口最大值

本文参考

出自LeetCode上的题库 —— 滑动窗口最大值,一般的双指针解法会导致时间超时,需要借助大根堆(大顶堆)实现

https://leetcode-cn.com/problems/sliding-window-maximum/

滑动窗口最大值问题

给定一个整数数组nums,有一个大小为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

解题思路

第一种解法是直接利用双指针,让$left$和$right$指针每次向右移动一个位置,每次移动后求取窗口内子串的最大值。但这种直观的思路会导致时间超时,若数组的长度为$n$,窗口长度为$k$,时间复杂度为$O(nk)$

第二种解法是借助python的heapq小根堆函数库,如果需要大根堆的效果,只需对数组的每个值取负值。创建堆的时间复杂度为$O(n)$,修改堆的时间复杂度为$O(logn)$,这时可能会问,我每次从堆里删除不在窗口中的元素,再向堆里插入窗口中新增的元素,还是很耗时啊?实际上,我们不需要在窗口移动时,每次都删除堆中不需要的元素,只要保证堆顶的元素在窗口中就行,从而减少堆的pop操作

双指针解法(超时)

class Solution:
  def
max_sliding_window(self, nums: List[int], k: int) -> List[int]:
    left = 0
    right = k
    ans = list()
    while right <= len(nums):
      curr = max(nums[left:right])
      ans.append(curr)
      left += 1
      right += 1
    return ans

大根堆解法(nlogn)

def max_sliding_window(self, nums: List[int], k: int) -> List[int]:
  n = len(nums)
  # 默认是小根堆,添加负号变成大根堆 

  q = [(-nums[i], i) for i in range(k)]
  heapq.heapify(q)

  ans = [-q[0][0]]
  for i in range(k, n):
    heapq.heappush(q, (-nums[i], i))
    # 不断地移除堆顶的元素,直到其确实出现在滑动窗口中 

    while q[0][1] <= i - k:
      heapq.heappop(q)
      ans.append(-q[0][0])
  return ans

posted @ 2022-03-10 19:59  咕~咕咕  阅读(70)  评论(0编辑  收藏  举报