Go语言滑动窗口最大值的实现示例

 更新时间:2025年08月14日 11:07:23   作者:程序员爱钓鱼  
本文主要介绍了Go语言滑动窗口最大值的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

在高性能计算、数据分析、监控系统等应用中,实时处理数据流是一项基础能力。其中一个典型的需求是:在一个不断移动的区间(窗口)内,快速求出当前区间的最大值。这就是我们今天要实现的算法——滑动窗口最大值(Sliding Window Maximum)

一、问题描述与示例

给定一个整数数组 nums 和一个整数 k(窗口大小),我们需要输出一个数组,表示从左到右依次滑动窗口后,每个窗口中的最大值。

示例

输入:
nums = [1,3,-1,-3,5,3,6,7], k = 3

输出:
[3,3,5,5,6,7]

解释:

  • 窗口 [1,3,-1] → 最大值 3
  • 窗口 [3,-1,-3] → 最大值 3
  • 窗口 [-1,-3,5] → 最大值 5
  • 窗口 [-3,5,3] → 最大值 5
  • 窗口 [5,3,6] → 最大值 6
  • 窗口 [3,6,7] → 最大值 7

二、常见解法分析

1. 暴力解法(Brute Force)

最直观的方式是:每次滑动窗口后,遍历窗口中所有元素,找最大值。

func MaxSlidingWindowBrute(nums []int, k int) []int {
    var result []int
    for i := 0; i <= len(nums)-k; i++ {
        maxVal := nums[i]
        for j := i; j < i+k; j++ {
            if nums[j] > maxVal {
                maxVal = nums[j]
            }
        }
        result = append(result, maxVal)
    }
    return result
}

复杂度分析:

  • 时间复杂度:O(n*k)
  • 空间复杂度:O(1)

当 n 较大、k 较大时,这种方法性能较差。例如 n=10^5, k=500 时,暴力解法会非常慢。

2. 优化解法:单调队列(Monotonic Queue)

为了提升效率,我们可以使用双端队列(Deque)实现一个单调递减队列

核心思想

  • 队列存下标,而非值;
  • 队列内下标对应的值保持从队头到队尾递减,这样队首元素就是当前窗口最大值;
  • 每次新元素加入时:
    • 移除队尾小于当前值的下标;
    • 将当前下标加入队尾;
  • 检查队首是否超出窗口范围(deque[0] <= i - k),如果是则弹出。

这种方式保证了每个元素最多进出队列一次,整体时间复杂度为 O(n)。

三、Go语言代码实现(单调队列法)

package main

import (
    "fmt"
)

// MaxSlidingWindow 求解滑动窗口最大值
func MaxSlidingWindow(nums []int, k int) []int {
    if len(nums) == 0 || k == 0 {
        return []int{}
    }

    var result []int
    deque := []int{} // 存储下标

    for i, v := range nums {
        // 1. 移除队尾比当前元素小的下标
        for len(deque) > 0 && nums[deque[len(deque)-1]] < v {
            deque = deque[:len(deque)-1]
        }
        // 2. 添加当前元素下标
        deque = append(deque, i)

        // 3. 移除超出窗口范围的队首下标
        if deque[0] <= i-k {
            deque = deque[1:]
        }

        // 4. 当窗口形成后,将队首对应的值加入结果
        if i >= k-1 {
            result = append(result, nums[deque[0]])
        }
    }

    return result
}

func main() {
    nums := []int{1, 3, -1, -3, 5, 3, 6, 7}
    k := 3
    fmt.Println(MaxSlidingWindow(nums, k)) // [3 3 5 5 6 7]
}

四、关键步骤详解

  • 队列存下标而非值
    • 方便判断是否超出窗口范围;
    • 通过 nums[deque[0]] 获取最大值。
  • 保持递减顺序
    • 只有比当前值大的元素才可能在未来成为最大值;
    • 小于当前值的元素直接被淘汰。
  • 滑动与淘汰
    • 每次滑动都要检查队首是否还在窗口内;
    • 淘汰不在窗口范围的下标。

五、复杂度分析

  • 时间复杂度:O(n)
    每个元素最多被加入和移出队列一次。
  • 空间复杂度:O(k)
    队列最多存储 k 个下标。

六、工程应用场景

  • 系统性能监控
    • 在固定时间窗口内找CPU利用率的峰值。
  • 金融数据分析
    • 计算股票价格的滑动最大值,用于趋势判断。
  • 信号处理
    • 检测信号峰值,常用于音频分析、网络流量监控。

七、常见变种

  • 滑动窗口最小值:只需将单调递减队列改为递增队列。
  • 滑动窗口平均值/中位数:需要更复杂的数据结构(如堆、平衡树)。
  • 二维滑动窗口最大值:可扩展至图像处理,如卷积操作。

八、测试与验证

我们可以编写单元测试:

func TestMaxSlidingWindow(t *testing.T) {
    tests := []struct {
        nums []int
        k    int
        want []int
    }{
        {[]int{1,3,-1,-3,5,3,6,7}, 3, []int{3,3,5,5,6,7}},
        {[]int{9,10,9,-7,-4,-8,2,-6}, 5, []int{10,10,9,2}},
    }

    for _, tt := range tests {
        got := MaxSlidingWindow(tt.nums, tt.k)
        if !reflect.DeepEqual(got, tt.want) {
            t.Errorf("nums=%v k=%d got=%v want=%v", tt.nums, tt.k, got, tt.want)
        }
    }
}

九、总结

  • 滑动窗口最大值问题是经典的单调队列应用;
  • 暴力解法易实现但性能低;
  • 单调队列解法能在 O(n) 时间内解决问题,适合大数据场景;
  • 掌握该技巧有助于解决其他类似的区间统计问题。

 到此这篇关于Go语言滑动窗口最大值的实现示例的文章就介绍到这了,更多相关Go语言滑动窗口最大值内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 用GO实现IP门禁优化网络流量管理

    用GO实现IP门禁优化网络流量管理

    这篇文章主要为大家介绍了用GO实现IP门禁优化网络流量管理,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • GoLang之使用Context控制请求超时的实现

    GoLang之使用Context控制请求超时的实现

    这篇文章主要介绍了GoLang之使用Context控制请求超时的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-04-04
  • Go语言切片(Slice)深度剖析与应用实战

    Go语言切片(Slice)深度剖析与应用实战

    在Go语言中,切片(Slice)是一种非常强大且灵活的数据结构,它基于数组但又提供了动态调整大小的能力,本文将结合实际案例,详细介绍Go语言中切片的声明、初始化、操作、扩容等用法,需要的朋友可以参考下
    2024-09-09
  • Go利用反射reflect实现获取接口变量信息

    Go利用反射reflect实现获取接口变量信息

    反射是通过实体对象获取反射对象(Value、Type),然后可以操作相应的方法。本文将利用Go语言中的反射reflect实现获取接口变量信息,需要的可以参考一下
    2022-05-05
  • docker如何安装部署golang应用程序

    docker如何安装部署golang应用程序

    这篇文章主要为大家介绍了docker如何安装部署golang应用程序详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • go语言通过管道连接两个命令行进程的方法

    go语言通过管道连接两个命令行进程的方法

    这篇文章主要介绍了go语言通过管道连接两个命令行进程的方法,实例分析了Go语言操作命令行进程的技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-03-03
  • GoLang调用链可视化go-callvis使用介绍

    GoLang调用链可视化go-callvis使用介绍

    与链路追踪(Tracing)不同,Tracing关注复杂的分布式环境中各个服务节点间的调用关系,主要用于服务治理。而我们本次探索的代码调用链路则是代码方法级别的调用关系,主要用于代码设计
    2023-02-02
  • golang结构化日志slog的用法简介

    golang结构化日志slog的用法简介

    日志是任何软件的重要组成部分,Go 提供了一个内置日志包(slog),在本文中,小编将简单介绍一下slog包的功能以及如何在 Go 应用程序中使用它,感兴趣的可以了解下
    2023-09-09
  • Go语言接口的嵌套的具体使用

    Go语言接口的嵌套的具体使用

    在Go语言中,不仅结构体与结构体之间可以嵌套,接口与接口间也可以通过嵌套创造出新的接口,本文主要介绍了Go语言接口的嵌套的具体使用,感兴趣的可以了解一下
    2023-04-04
  • golang实现webgis后端开发的步骤详解

    golang实现webgis后端开发的步骤详解

    这篇文章主要介绍如何用golang结合postgis数据库,使用gin、grom框架实现后端的MVC的接口搭建,文中有详细的流程步骤及代码示例,需要的朋友可以参考下
    2023-06-06

最新评论