Skip to content

在 Go 中实现滑动窗口可以利用双指针或者队列进行灵活调整。滑动窗口特别适合解决一维数据结构中的子区间问题,例如字符串和数组。以下是滑动窗口的详解,包括通用框架、经典题目,以及实际场景中的应用。


1. 滑动窗口的核心思想

滑动窗口的目的是维护一个动态区间,满足某些条件的同时尽量高效地更新结果:

  1. 扩展窗口:右指针 right 向右移动扩展窗口。
  2. 收缩窗口:左指针 left 向右移动缩小窗口。
  3. 记录结果:在满足条件时,更新解或记录窗口状态。

2. 滑动窗口通用框架(Go 实现)

滑动窗口的通用代码框架如下:

go
func slidingWindow(s string, t string) string {
    left, right := 0, 0            // 窗口左右指针
    window := make(map[byte]int)  // 当前窗口字符计数
    need := make(map[byte]int)    // 目标字符计数
    valid := 0                    // 满足条件的字符数

    // 初始化目标计数
    for i := 0; i < len(t); i++ {
        need[t[i]]++
    }

    // 滑动窗口
    for right < len(s) {
        c := s[right]
        right++ // 扩展窗口
        if need[c] > 0 {
            window[c]++
            if window[c] == need[c] {
                valid++
            }
        }

        // 判断是否收缩窗口
        for valid == len(need) {
            // 更新结果或其他逻辑

            // 左边字符移出窗口
            d := s[left]
            left++ // 收缩窗口
            if need[d] > 0 {
                if window[d] == need[d] {
                    valid--
                }
                window[d]--
            }
        }
    }

    return "" // 返回结果
}

3. 滑动窗口经典问题

3.1 最小覆盖子串

问题描述:在字符串 s 中找到包含 t 中所有字符的最小子串。

go
func minWindow(s string, t string) string {
    need := make(map[byte]int)
    window := make(map[byte]int)
    for i := 0; i < len(t); i++ {
        need[t[i]]++
    }

    left, right, valid := 0, 0, 0
    start, minLen := 0, len(s)+1

    for right < len(s) {
        c := s[right]
        right++
        if need[c] > 0 {
            window[c]++
            if window[c] == need[c] {
                valid++
            }
        }

        for valid == len(need) {
            if right-left < minLen {
                start = left
                minLen = right - left
            }
            d := s[left]
            left++
            if need[d] > 0 {
                if window[d] == need[d] {
                    valid--
                }
                window[d]--
            }
        }
    }

    if minLen == len(s)+1 {
        return ""
    }
    return s[start : start+minLen]
}

3.2 无重复字符的最长子串

问题描述:在字符串 s 中找到不含重复字符的最长子串。

go
func lengthOfLongestSubstring(s string) int {
    window := make(map[byte]int)
    left, right := 0, 0
    maxLength := 0

    for right < len(s) {
        c := s[right]
        right++
        window[c]++

        for window[c] > 1 { // 出现重复字符
            d := s[left]
            left++
            window[d]--
        }

        if right-left > maxLength {
            maxLength = right - left
        }
    }

    return maxLength
}

3.3 找所有字母异位词

问题描述:在字符串 s 中找到所有 p 的异位词(排列组合)的起始索引。

go
func findAnagrams(s string, p string) []int {
    need := make(map[byte]int)
    window := make(map[byte]int)
    for i := 0; i < len(p); i++ {
        need[p[i]]++
    }

    left, right, valid := 0, 0, 0
    res := []int{}

    for right < len(s) {
        c := s[right]
        right++
        if need[c] > 0 {
            window[c]++
            if window[c] == need[c] {
                valid++
            }
        }

        for right-left >= len(p) {
            if valid == len(need) {
                res = append(res, left)
            }
            d := s[left]
            left++
            if need[d] > 0 {
                if window[d] == need[d] {
                    valid--
                }
                window[d]--
            }
        }
    }

    return res
}

3.4 最大连续 1 的个数(带限制条件)

问题描述:数组中最多允许翻转 k 个 0,找到连续 1 的最大长度。

go
func longestOnes(nums []int, k int) int {
    left, right := 0, 0
    zeroCount := 0
    maxLen := 0

    for right < len(nums) {
        if nums[right] == 0 {
            zeroCount++
        }
        right++

        for zeroCount > k {
            if nums[left] == 0 {
                zeroCount--
            }
            left++
        }

        if right-left > maxLen {
            maxLen = right - left
        }
    }

    return maxLen
}

4. 使用场景

4.1 网络流量监控

  • 滑动窗口用于记录一段时间内的请求数量或流量,判断是否超出阈值。
  • 实现时可用队列记录每次请求的时间戳,动态调整窗口。

4.2 日志处理

  • 滑动窗口用于统计日志文件中,某段时间的特定事件发生频率。
  • 窗口动态调整以适配不同的时间范围。

4.3 股票价格分析

  • 滑动窗口用于计算某个时间窗口内的最大收益或价格波动范围。

4.4 视频流缓冲

  • 滑动窗口用于监控视频数据缓存量,动态调整缓冲区大小以避免卡顿。

滑动窗口是一种非常通用的算法技巧。在实际应用中,重点在于动态维护窗口的状态,根据场景需求设计扩展和收缩的条件。结合具体问题,可以进一步优化代码性能!如果有具体场景需要实现,可以继续讨论!