<think>好的,用户之前问过关于吃凉东西导致暴躁的原因,现在转到了Golang的滑动窗口算法题代码模板和变式。我需要先确定用户的需求。可能用户正在准备面试,需要算法方面的帮助。滑动窗口是常见的算法题类型,用户想要一个通用的代码模板,以及了解不同的变式,这样在遇到不同题目时可以灵活应用。
首先,我需要回忆滑动窗口算法的基本结构。滑动窗口通常用于解决数组/字符串的子数组、子串问题,比如最大/最小的满足条件的子数组。模板一般包括初始化左右指针、右指针右移、满足条件时收缩左指针、更新结果等步骤。
然后,用户还问到有多少种题目变式。常见的变式可能包括固定窗口大小、可变窗口大小、需要维护数据结构(如哈希表、前缀和)的情况,以及不同的条件判断,比如最长无重复子串、最小覆盖子串、找到所有字母异位词等。需要列举几种典型的变式,并给出对应的例子,帮助用户理解。
接下来,需要用Golang写出模板。要注意Golang的特性,比如使用for循环、map来维护字符计数等。代码模板需要简洁明了,关键部分加上注释,方便用户理解每个步骤的作用。同时,每个变式题目需要给出对应的LeetCode例题和代码示例,这样用户可以参考具体实现。
可能用户需要的是模板的通用性,所以在模板中需要指出哪些部分是需要根据题目调整的,比如如何判断窗口是否满足条件、如何更新结果等。同时,要提醒用户注意边界条件,比如右指针的移动时机、窗口收缩的条件等。
另外,用户可能对滑动窗口的不同变式数量感兴趣,需要总结常见的几种类型,并说明它们的区别,比如固定窗口和可变窗口的不同应用场景。这样用户在遇到新题时,可以判断属于哪种类型,从而套用相应的解题思路。
最后,确保回答结构清晰,分点列出模板和变式,每个部分都有示例和解释,方便用户理解和应用。同时,避免使用过于专业的术语,保持语言简洁,让不同水平的用户都能受益。</think>以下是 Golang 的滑动窗口算法模板及常见变式总结:
---
### **滑动窗口模板代码**
```go
func slidingWindow(s string) int {
left, right := 0, 0 // 左右指针
window := make(map[byte]int) // 窗口数据存储(根据题目调整类型)
result := 0 // 结果(可能是长度、数量等)
for right < len(s) {
c := s[right] // 右侧进入窗口的字符
right++ // 右指针右移
window[c]++ // 更新窗口数据
// 当窗口不满足条件时收缩左边界
for 需要收缩的条件 { // 根据题目定义条件
d := s[left] // 左侧移出窗口的字符
left++ // 左指针右移
window[d]-- // 更新窗口数据
if window[d] == 0 {
delete(window, d)
}
}
// 更新结果(可能需要在此处判断是否满足条件)
result = max(result, right - left) // 或其他计算方式
}
return result
}
```
---
### **常见变式类型(6大类)**
#### **1. 固定窗口大小**
- **特征**:窗口长度固定为 `k`
- **例题**:643. 子数组最大平均数([LeetCode 643](https://leetcode.com/problems/maximum-average-subarray-i/))
```go
func findMaxAverage(nums []int, k int) float64 {
sum := 0
for i := 0; i < k; i++ { sum += nums[i] }
maxSum := sum
for i := k; i < len(nums); i++ {
sum += nums[i] - nums[i - k]
if sum > maxSum { maxSum = sum }
}
return float64(maxSum) / float64(k)
}
```
#### **2. 最小窗口子串**
- **特征**:找到满足条件的最小窗口
- **例题**:76. 最小覆盖子串([LeetCode 76](https://leetcode.com/problems/minimum-window-substring/))
```go
func minWindow(s string, t string) string {
need := make(map[byte]int)
for i := range t { need[t[i]]++ }
left, valid := 0, 0
resStart, minLen := -1, math.MaxInt32
for right := 0; right < len(s); right++ {
if need[s[right]] > 0 { valid++ }
need[s[right]]--
for valid == len(t) {
if right - left + 1 < minLen {
minLen = right - left + 1
resStart = left
}
need[s[left]]++
if need[s[left]] > 0 { valid-- }
left++
}
}
if resStart == -1 { return "" }
return s[resStart : resStart + minLen]
}
```
#### **3. 最长无重复子串**
- **特征**:窗口内元素唯一
- **例题**:3. 无重复字符的最长子串([LeetCode 3](https://leetcode.com/problems/longest-substring-without-repeating-characters/))
```go
func lengthOfLongestSubstring(s string) int {
window := make(map[byte]int)
left, maxLen := 0, 0
for right := 0; right < len(s); right++ {
c := s[right]
window[c]++
for window[c] > 1 {
d := s[left]
window[d]--
left++
}
maxLen = max(maxLen, right - left + 1)
}
return maxLen
}
```
#### **4. 计数型窗口**
- **特征**:统计满足条件的窗口数量
- **例题**:567. 字符串的排列([LeetCode 567](https://leetcode.com/problems/permutation-in-string/))
```go
func checkInclusion(s1 string, s2 string) bool {
need := make(map[byte]int)
for i := range s1 { need[s1[i]]++ }
left, valid := 0, 0
window := make(map[byte]int)
for right := 0; right < len(s2); right++ {
c := s2[right]
if need[c] > 0 {
window[c]++
if window[c] == need[c] { valid++ }
}
for right - left + 1 >= len(s1) {
if valid == len(need) { return true }
d := s2[left]
if need[d] > 0 {
if window[d] == need[d] { valid-- }
window[d]--
}
left++
}
}
return false
}
```
#### **5. 多指针窗口**
- **特征**:需要维护多个指针或复杂状态
- **例题**:930. 和相同的二元子数组([LeetCode 930](https://leetcode.com/problems/binary-subarrays-with-sum/))
```go
func numSubarraysWithSum(nums []int, goal int) int {
return atMost(nums, goal) - atMost(nums, goal-1)
}
func atMost(nums []int, k int) int {
if k < 0 { return 0 }
left, sum, count := 0, 0, 0
for right := 0; right < len(nums); right++ {
sum += nums[right]
for sum > k {
sum -= nums[left]
left++
}
count += right - left + 1
}
return count
}
```
#### **6. 前缀和+滑动窗口**
- **特征**:处理负数或需要快速区间计算
- **例题**:325. 和等于 k 的最长子数组长度([LeetCode 325](https://leetcode.com/problems/maximum-size-subarray-sum-equals-k/))
```go
func maxSubArrayLen(nums []int, k int) int {
prefixSum := make(map[int]int)
prefixSum[0] = -1
sum, maxLen := 0, 0
for i := 0; i < len(nums); i++ {
sum += nums[i]
if idx, ok := prefixSum[sum - k]; ok {
maxLen = max(maxLen, i - idx)
}
if _, ok := prefixSum[sum]; !ok {
prefixSum[sum] = i
}
}
return maxLen
}
```
---
### **关键技巧**
1. **窗口收缩条件**:定义何时移动左指针
2. **数据结构选择**:根据需求选择哈希表、数组或堆
3. **状态维护**:有效管理窗口内的数据状态
4. **边界处理**:特别注意空窗口、单元素窗口等情况
通过组合这些模式,可以解决 90% 以上的滑动窗口问题。建议先从经典题(如 LeetCode 3/76/209/424)开始练习。