面试题型系列:滑动窗口技巧

本文是公众号读者上山打老虎的第二篇原创投稿,主要内容是讲解算法技巧之滑动窗口。上山兄一直保持着刷题的习惯,并形成了自己的一套做题心得,当然他也是无情的offer收割机。同时上山兄会持续给本号投稿算法类文章,代码示例均采用Go语言,希望该算法系列文章有助读者更好地备战面试。

在数组中查找一个数,可以使用二分法查找,但是算法问题中还有一些是在数组(或字符串)中查找一个子区间,这时滑动窗口就是一种很好的解决思路。

很多同学学过滑动窗口算法,但是一做题就错,这是因为有很多细节问题会在解答时出错,本文将依次介绍该算法的模板,易错点,分类题型,希望读者看完之后能极大的提高做题速度以及准确度。

什么是滑动窗口

概念

滑动窗口是双指针算法的一种,利用双指针在数组单一方向滑动,形成一个子区间,对子区间进行剪枝,最终得出满足条件的解。

过程

window中元素未完全包含target时,right向右滑动


此时window包含target中所有元素,将left向右滑动


Go代码模板
 1func checkInclusion(nums []int, target []int) bool {
 2  window := make(map[byte]int, len(nums))
 3    left, right := 0, 0
 4
 5    for right < len(nums) {
 6        c := nums[right]
 7        window[c]++
 8        //right右滑变量变化
 9        right++
10
11        for 满足left右滑条件 {
12            //判断区间是否满足条件(可能在left右滑的前中后)
13
14            d := nums[left]
15            //left右滑变量的改动
16
17            left++
18        }
19    }
20    return false
21}
易错点
  • left和right滑动时,容易漏掉某个变量的值变化,这里教大家一个技巧,每次写完复查前面声明的变量,在滑动时是否改变

  • left右滑动时,先判断是否满足条件,再做变量值的变化,颠倒会导致第一次满足条件的解漏掉

例题解析

值匹配

这道题是求最长子串,这里我们需要敏锐地捕捉到“子串”关键词。该题中,我们把字符串当做数组,“子串”就是子区间,这赤裸裸的就是滑动窗口的题型呀。

废话不多说,直接先上模板!

 1func lengthOfLongestSubstring(s string) int {
 2    lens := len(s)
 3    window := make(map[byte]int, lens)
 4  left, right := 0, 0
 5
 6    for right < lens{
 7        c := nums[right]
 8        window[c]++
 9        //right右滑变量变化
10        right++
11
12        for 满足left右滑条件 {
13            //判断区间是否满足条件(可能在left右滑的前中后)
14
15            d := nums[left]
16            //left右滑变量的改动
17
18            left++
19        }
20    }
21    return false
22}

分析

  1. 满足left右滑动的条件:无重复字符,即右滑时window容器中每个元素值<=1

  2. 是否更新结果值条件:在window中无重复的元素后,即for循环之后,判断res-left与之前无重复字符串最大长度(res)之间关系

  3. right右滑动变量变化:参照声明的四个变量,这题只有winow和right变化

  4. left右滑动变量变化:参照声明的四个变量,这题只有winow和left变化

 1func lengthOfLongestSubstring(s string) int {
 2    lens := len(s)
 3    window := make(map[byte]int, lens)
 4    left, right, res := 0, 0, 0
 5
 6  //right右滑动
 7    for right < lens {
 8        b := s[right]
 9        window[b]++
10        right++
11    //left右滑动
12        for window[b] > 1 {
13            c := s[left]
14            window[c]--
15            left++
16        }
17    //判断区间是否满足条件
18        if right-left > res {
19            res = right - left
20        }
21    }
22    return res
23}
区间匹配

很明显这又是一个求子串的问题,分析如下

  1. 满足left右滑动的条件:这题和前言中题类似,设一个target切片记录目标区间每个元素的个数[A:1,B:1,C:1],当window中所有目标元素个数(vaild)都达到target要求时,left右滑缩小区间

  2. 是否更新结果值条件:在left右滑时,即for循环中判断当前right-left与之前最小覆盖子串之间关系

  3. right右滑动变量变化:参照声明的变量有当前区间window,达标元素个数vaild,窗口右边界right

  4. left右滑动变量变化:参照声明的变量有起始位置start,结果字符串长度res,达标元素个数vaild,当前区间window,窗口左边界left

 1func minWindow(s string, t string) string {
 2  //变量初始化
 3    lens := len(s)
 4    lent := len(t)
 5    window := make(map[byte]int, lens)
 6    target := make(map[byte]int, lent)
 7    left, right, vaild := 0, 0, 0
 8    res := lens
 9    start := -1
10
11    for i := 0; i < lent; i++ {
12        target[t[i]]++
13    }
14
15  //right右滑动
16    for right < lens {
17        b := s[right]
18        window[b]++
19        if target[b] == window[b] {
20            vaild++
21        }
22        right++
23
24    //left右滑动
25        for vaild == len(target) {
26            c := s[left]
27      //是否更新结果值
28            if res >= right-left {
29                start = left
30                res = right - left
31            }
32
33            if window[c] == target[c] {
34                vaild--
35            }
36      window[c]--
37            left++
38        }
39    }
40    if start == -1 {
41        return ""
42    }
43    return s[start : start+res]
44}

总结

  1. 题型:求子串、求子数组,在这样的题目关键字中,我们可以考虑通过双指针单向滑动剪枝出满足条件的区间

  2. 记住模板:一容(window),二变(left,right),三扩(right右移),四缩(left右移)

  3. 思路:俩个条件(left右滑条件,是否更新结果值条件),俩个变化(right右滑动变量变化,left右滑动变量变化)

  4. 易错点:例如第二题每次滑动需要更新的变量很多,稍有不慎就会少更新某个变量,每次对照开始声明的变量可以万无一失

往期推荐

机器铃砍菜刀

欢迎添加小菜刀微信

加入Golang分享群学习交流!

感谢你的点赞在看哦~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值