【LeetCode】239.滑动窗口最大值

题目链接:239. 滑动窗口最大值

题目描述:

数据范围:

go 复制代码
1 <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4
1 <= k <= nums.length

思路1:采用双指针去维护这个窗口边界,同时每次滑动都遍历计算窗口内的最大值,这种方式时间复杂度O(n*k),显然时间复杂度比较高,数据范围也比较大,不太能接受。

思路2:仍然采用双指针来维护窗口边界,主要优化是这个O(K)复杂度的遍历,这里可以用一个K大小的大根堆来维护窗口内的最大值,这里由于不仅看值,还要看下标,因此堆中的每个节点可以存储是一个二元组<元素,下标>,但仔细一想其实不用,我们只需要存储下标就行了,堆比较的时候还是按照值来调整下标在堆中的顺序;每次滑动窗口将当前的元素下标加入堆中,并且检查堆顶元素的下标如果不在窗口内了(堆顶下标+k < =i(当前窗口右边界)),那么就需要不断移除堆顶元素,直到满足当前堆中的最大元素下标在窗口内,每次记录答案都直接从堆中取当前的最大值,也就是堆顶的值,这里调整堆复杂度是O(logK),因此总的复杂度为O(nlogK)

golang里面实现堆排主要是需要实现以下几个方法,注意push和pop实现是指针方法。

go 复制代码
Len()
Less(i,j int) bool
Swap(i,j int)
Push(v interface{}) 指针
Pop() interface{} 指针

实现1:用sort.IntSlice可以少实现两个方法,它本身有序

go 复制代码
// 实现一个大根堆,堆顶元素最大,只存储下标即可,这里有sort,所以我们实现less、push和pop方法即可
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(x interface{}) {
    h.IntSlice = append(h.IntSlice, x.(int))
}

func (h *hp) Pop() interface{} {
    old := h.IntSlice
    n := len(old)
    res := old[n-1]
    h.IntSlice = old[:n-1]
    return res
}

func maxSlidingWindow(nums []int, k int) []int {
    if k == 1 {
        return nums
    }
    a = nums
    h := &hp{make([]int,k)}
    // 入堆
    for i := 0; i < k; i++ {
       h.IntSlice[i] = i
    }
    heap.Init(h)

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

实现2:用数组实现堆

go 复制代码
type hp []int // 记录下标
var a []int
func (h hp) Less(i, j int) bool {
    return a[h[i]] > a[h[j]]
}
func (h *hp) Push(x interface{}) {
    *h = append(*h, x.(int))
}

func (h hp) Len() int {
    return len(h)
}

func (h hp) Swap(i, j int) {
   h[i], h[j] = h[j], h[i]
}

func (h *hp) Pop() interface{} {
    old := *h
    n := len(old)
    res := old[n-1]
    *h = old[:n-1]
    return res
}

func maxSlidingWindow(nums []int, k int) []int {
    if k == 1 {
        return nums
    }
    a = nums
    h := &hp{}
    // 入堆
    for i := 0; i < k; i++ {
      heap.Push(h,i)
    }
    heap.Init(h)

    ans := make([]int, 0, len(nums)-1)
    ans = append(ans, nums[(*h)[0]])
    for i := k; i < len(nums) ; i++{
        heap.Push(h, i)
        for (*h)[0] <= i-k { // 当前最大元素的下标不在区间内,则弹出
            heap.Pop(h)
        }
        ans = append(ans, nums[(*h)[0]])
    }
    return ans
}

思路3:我们用堆主要是为了维护这个窗口内最大值,那么我们换一种思路,用一个单调队列维护窗口的最大值,顺序我们按照从左往右,单调递减。对前k个元素过一遍单调队列的时候,队头(左边)始终保持最大值,因此,新进一个元素的时候,我们只需要和队尾(右边)的元素比较,如果大于队尾的元素,就移除掉队尾的元素,因此已经没用了。当窗口移动的时候,为了保证队列中的元素都在窗口内,我们需要检查一下队首元素的下标+k的值是否小于等于i,如果小于,就该去掉队头的元素了。因此队列中元素满足两个特性:

  • 从左往右值是单调递减的。
  • 从做往右,下标是单调递增的。

我们push的时候只需要按照这两个特定来维护队列即可,窗口滑动的时候,我们还需要检查队首元素的下标是否在当前窗口内,不在就删除队首元素,直到在窗口内。每个元素在遍历的时候有且仅会进出一次队列,因此实际上时间复杂度为O(n)。

go 复制代码
func maxSlidingWindow(nums []int, k int) []int {
    queue := []int{} // 单调队列维护下标
    push := func(i int) {
        for len(queue) > 0 && nums[i] >= nums[queue[len(queue)-1]] {
            queue = queue[:len(queue)-1]
        }
        queue = append(queue,i)
    }
    for i := 0; i < k; i++ {
        push(i)
    }
    n := len(nums)
    ans := make([]int, 0, n-k+1)
    ans = append(ans, nums[queue[0]])
    for i := k; i < len(nums); i++ {
        push(i)
        for queue[0] <= i-k { // 删除队头元素
            queue = queue[1:]
        }
        ans  = append(ans, nums[queue[0]])
    }
    return ans
}
相关推荐
MartinYeung514 分钟前
[论文学习]DP2Unlearning:高效且具保证的大型语言模型遗忘框架(基于差分隐私的 LLM Unlearning 方法)
学习·算法·语言模型
Tian_Hang26 分钟前
C++原型模式(Protype)
开发语言·c++·算法
bIo7lyA8v27 分钟前
算法复杂度的渐进分析与实际运行时间的差异的技术8
算法
一切皆是因缘际会1 小时前
LLM轻量化联邦微调机理
数据结构·人工智能·数学建模·ai
jingling5551 小时前
go | 环境安装和快速入门
开发语言·后端·golang
yuan199971 小时前
欧拉梁静力与屈曲计算的 MATLAB 实现(有限差分法 + 解析解)
开发语言·算法·matlab
玖玥拾2 小时前
C/C++ 数据结构(六)链表迭代器与底层
c语言·数据结构·c++·链表·stl库
牛油果子哥q2 小时前
AVL平衡树与红黑树深度精讲对比,平衡因子、四大旋转原理、着色规则、平衡策略、性能差异与面试手撕全解
数据结构·c++·面试
汉克老师2 小时前
GESP7级C++考试语法知识(二、指数函数(3、综合练习)
c++·算法·数学建模·指数函数·gesp7级·复利
林间码客3 小时前
04 ROC曲线与AUC:从零开始手动计算
大数据·人工智能·算法