pyqt 只能打开子窗口一次_Leetcode刷题总结之滑动窗口法(尺取法)

791eb10bb28fc664d1f75d8a3419e57c.png

介绍

滑动窗口法,也叫尺取法(可能也不一定相等,大概就是这样 =。=),可以用来解决一些查找满足一定条件的连续区间的性质(长度等)的问题。由于区间连续,因此当区间发生变化时,可以通过旧有的计算结果对搜索空间进行剪枝,这样便减少了重复计算,降低了时间复杂度。往往类似于“请找到满足xx的最x的区间(子串、子数组)的xx”这类问题都可以使用该方法进行解决。

引入的小例子

Leetcode 209. 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。

示例: 

输入: s = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。

这道题目最简单的解法自然是枚举每个数组起点和终点,这种解法的时间复杂度是O(N^2)

def 

通过分析可以发现,这种解法进行了很多重复计算,首先是对于状态的重复计算,比如当start为0时,我们计算了区间[0,0], [0,1], [0,2],...等的和,但当start为1时,我们又重新计算了区间[1,1], [1,2], ....,的和,但事实上,这些区间的值是可以根据上一次计算的结果直接得到的,如区间[1,2]等于区间[0,2]减去nums[0]的值。换句话说,我们可以根据之前计算得到的结果来推断还未进行计算的结果,这也为剪枝带来了可能。

考虑这样一个例子,给定数组为[2,3,1,2,4,3],并给定要求的最小和s为7,通过第一次枚举,我们得知子数组[2,3],[2,3,1]都是小于7的,那我们也就没有必要在接下来的阶段对子数组[3],子数组[3,1]进行枚举检查了,因为他们的和一定是小于7的。若实现了这种剪枝,时间复杂度便可以得到大幅的优化。

那么如何实现这样的剪枝呢?考虑这样一种情形,数轴上存在一个滑动窗口,假设其左右端点分别为L和R。首先我们移动R,使得滑动窗口的区间满足给定的条件,然后我们再移动L,直到滑动区间不再满足给定的条件,如此循环往复,并在其过程中记录最优值。继续之前的例子,如图所示,过程如下:

  1. 滑动窗口的长度为0,位于数轴的最左端
  2. 滑动窗口右端R开始移动,直到区间满足给定的条件,也就是和大于7,停止于第三个元素2,记录下来当前的最优长度为4
  3. 滑动窗口左端L开始移动,并停止于第一个元素3,此时区间和为6,使得区间和不满足给定的条件
  4. 滑动窗口右端R继续移动,停止于第四个元素4,在过程中,最优长度仍然为4
  5. 滑动窗口左端L移动至第三个元素2,过程中更新最优长度为3
  6. 滑动窗口右端R移动至最后一个元素3,
  7. 滑动窗口左端L移动至最后一个元素,并在过程中更新最优长度为2

3dbd853353ffac0f85d64411ef8fbf83.png
滑动窗口法Leetcode209的过程示意图

滑动窗口法的大体框架

通过归纳,我们可以勾勒出滑动窗口法的大体框架(只是基本框架,根据不同的问题应适当变动,重在把握精神)

初始化窗口端点

滑动窗口法实例

Leetcode 209. 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。

示例: 

输入: s = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。

这道题是上面讲解过的题目,这里套用之前提出的框架再讲一遍。我们设置一个状态为summation,表示当前区间的和,而状态满足的条件是summation >= s,寻找最优值则是去比较当前的最优值以及目前滑动窗口的长度。代入框架即得到了求解该问题的程序:

def 

Leetcode 3. 无重复字符的最长子串

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:

输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:

输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

这道题是寻找无重复字符的最长子串,我们设置一个set来保存当前区间内的字符。对于右端点而言,S[R]不在set当中即满足条件。对于左端点而言,只要让左端点移动到目前S[R]的值第一次出现的位置后面即可,也就是说,不让滑动窗口包含重复的字符(因为重复的字符一定是当前右端点指向的字符)。这道题和前面一道题不同的地方在于,前一道题右端点是从不满足给定条件到移动满足给定条件,而这道题则相反。因此右端点会移动到第一次不满足条件的位置,而左端点则移动到再一次满足条件的位置。代码如下:

def 

Leetcode 1004. 最大连续1的个数 III

给定一个由若干 0 和 1 组成的数组 A,我们最多可以将 K 个值从 0 变成 1 。
返回仅包含 1 的最长(连续)子数组的长度。

示例 1:

输入:A = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释: 
[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。

示例 2:

输入:A = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
输出:10
解释:
[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 10。

这道题和上一题一样,右端点移动到第一次不满足条件的位置,而左端点则移动到再一次满足条件的位置。需要满足的条件是,K值大于等于0。当碰到零时,K减去1。代码如下所示:

def 

总结

滑动窗口法可以用来解决一些查找满足一定条件的连续区间的性质(长度等)问题,个人认为可以看做是一种双指针方法的特例,两个指针都起始于原点,并一前一后向终点前进。还有一种双指针方法,其两个指针一始一终,并相向靠近,这种方法的内在思想和滑动窗口也非常类似,如Leetcode11. 盛最多水的容器就可以使用这种解法求解。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值