题目大意
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到 在滑动窗口 k 内的数字。滑动窗口每次只向右移动一位。返回滑动窗口最大值。
解题思路
给定一个数组和一个窗口为 K 的窗口,当窗口从数组左边滑动到数组右边的时候,输出每次移动窗口后,在窗口内的最大值。

  • 这道题最暴力的方法就是 2 层循环,时间复杂度 O(n * K)。
  • 另一种思路是用优先队列,每次窗口以后的时候都向优先队列里面新增节点,并删除一个节点。时间复杂度是 O(n * log n)
  • 最优的解法是用双端队列,队列的一边永远都存的是窗口的最大值,队列的另外一个边存的是比最 大值小的值。队列中最大值左边的所有值都出队。在保证了双端队列的一边即是最大值以后,时间 复杂度是 O(n),空间复杂度是 O(K)
  1. //暴力解
  2. func maxSlidingWindow(nums []int ,k int) []int {
  3. res := make([]int, 0, k) //存储最大值得数组
  4. n := len(nums)
  5. if n == 0 {
  6. return []int{}
  7. }
  8. for i := 0; i <= n-k; i++ { //移动窗口
  9. max := nums[i] //暂定窗口头一个为最大值
  10. for j := 1; j< k; j++ { //遍历k窗口里的值
  11. if max < nums[i+j] {
  12. max = nums[i+j]
  13. }
  14. }
  15. res = append(res,max)
  16. }
  17. return res
  18. }

解题思路二,单调栈,双端队列

从左到右滑动窗口
维护一个单调递减的队列
对于每个元素 如果大于等于队列的最后一个元素,就把队尾的元素去除,直到队列为空或者队尾元素大于当前元素,再把当前元素追加到队尾
队列第一个元素就是最大值:deque+index 实现

头尾尾头原则!还有一个FIFO 🏀队长原则,先进的小,将一直当队长。
参考:视频链接
image.png
image.png

//1,单调队列解法,双端队列 deque+index 实现
//定义一个单调递增的队列,保存队列中的index索引
func maxSlidingWindow(nums []int, k int) []int {
    if len(nums) == 0 {
        return nil
    }

    var Q = make([]int, 0 ,len(nums))   //定义队列
    res := make([]int, len(nums)-k+1)   //定义结果,就是多少个k组

    for i := 0; i <len(nums); i++ {
        for len(Q) != 0 && nums[i]> nums[Q[len(Q)-1]] {
            Q = Q[:len(Q)-1]
        }

        Q = append(Q,i) //当前元素入栈

        if Q[0] == i-k {    //窗口元素离开左【】队首时,栈里第一个元素出栈
            Q = Q[1:]
        }

        if i+1-k >=0 {  //窗口里有k个元素
            res[i+1-k] = nums[Q[0]]
        }
    }
    return res
}
// 解法二 双端队列 Deque
func maxSlidingWindow(nums []int, k int) []int {
    if len(nums) == 0 || len(nums) < k {    //队列为空,或者没有数前进了,就结束了
        return make([]int, 0)
    }

    window := make([]int, 0, k)
    res := make([]int, 0, len(nums)-k+1)    //存储最大值

    for i, v := range nums {
        if i >= k && window[0] <= i-k {
            window = window[1:len(window)]
        }
        for len(window) > 0 && nums[window[len(window)-1]] < v {
             window = window[0 : len(window)-1]
        }

        window = append(window, i)
        if i >= k-1 {
            res = append(res, nums[window[0]])
        }
    }
    return res
}

方法三:优先队列,堆

对于「最大值」,我们可以想到一种非常合适的数据结构,那就是优先队列(堆),其中的大根堆可以帮助我们实时维护一系列元素中的最大值。

//队列解法
var a []int
type hp struct{ sort.IntSlice }
func (h hp) Less(i, j int) bool  { return a[h.IntSlice[i]] > a[h.IntSlice[j]] }
func (h *hp) Push(v interface{}) { h.IntSlice = append(h.IntSlice, v.(int)) }
func (h *hp) Pop() interface{}   { a := h.IntSlice; v := a[len(a)-1]; h.IntSlice = a[:len(a)-1]; return v }

func maxSlidingWindow(nums []int, k int) []int {
    a = nums
    q := &hp{make([]int, k)}
    for i := 0; i < k; i++ {
        q.IntSlice[i] = i
    }
    heap.Init(q)

    n := len(nums)
    ans := make([]int, 1, n-k+1)
    ans[0] = nums[q.IntSlice[0]]
    for i := k; i < n; i++ {
        heap.Push(q, i)
        for q.IntSlice[0] <= i-k {
            heap.Pop(q)
        }
        ans = append(ans, nums[q.IntSlice[0]])
    }
    return ans
}

image.png