在算法题目中,经常会遇到给定一个数组,找出符合某种特性的连续子数组。这时候可以采用滑动窗口的方法。
例如,给定数组A = [1,1,3,2,1,0,2],找出满足和小于等于5的最长连续子数组。
采用暴力的方法,需要用一层循环枚举子数组的起始位置,然后用第二层循环枚举子数组的长度,时间复杂度为O(N^2)。
采用滑动窗口的方式即可将算法复杂度降到O(N)。
其核心思想是,采用两个指针,分别指代子数组的左右边界。找到以每个坐标为起点的比之前长的子数组。以上述问题为例,刚开始,left=0, right = 0, ans =0 window_sum = 0
现在我们想要找到以[1]为起点,并且符合条件的最长子数组。
此时我们移动right,可以发现当right=3的时候,和就已经大于5了,所以以[1]为开始的最长子数组的长度是3,更新ans = 3,window_sum = 5
此时我们将left向右移动,此时可以发现指代的是[1,3,2]这样一个数组,我们可以直接认为以第二个[1]为开始,并不存在最优解。这是因为,如果想要比之前的3还要长,至少要从现在这个2开始,就算之前[1,3]满足条件,这也毫无意义,我们想要找的是比之前长的子数组,所以我们再将left向右移动,直到结束。
可以发现这种算法的魅力所在是left和right一直都在向右移动,所以说复杂度为2*O(N)。其通过不断寻找最长这样一个思想,每次限定了right最小的起始位置,从而舍弃了很多一定不是最优解的计算,达到了O(N)的时间复杂度。
算法如下:
window_sum = 0
left = 0
length = 0
for right in range(len(nums)):
window_sum += nums[right]
while window_sum > cost:
length = max(length, right-left)
window_sum -= nums[left]
left += 1
return max(length, right - left + 1)
其中注意一点的是,如果最后的结果包含最后一位数字,也就是最后没有进入while循环来更新length,所以我们需要在外层额外判断这种情况。
有了找到小于等于K的最长连续子数组,我们自然会想到 找到大于等于k的最短连续子数组 这样一个问题。
和上面的思想同理,我们首先找到起始位是0的最短子数组,然后将left向左边滑动,right保持不变。这是因为如果存在一个新的右界<right,并满足条件,这会导致之前以0为开始子数组一定会包含它,从而使得其大于等于k,但是这和直到遇到right都小于k的事实相矛盾,所以至少要从right开始。
值得注意的是,由于是满足条件进入的while,所以长度是right-left + 1,并且没有外面的额外判断。
代码如下:
window_sum = 0
left = 0
length = float('inf')
for right in range(len(nums)):
window_sum += nums[right]
while window_sum >= cost:
length = min(length, right-left+1)
window_sum -= nums[left]
left += 1
return length
本文深入探讨了滑动窗口算法在查找连续子数组问题中的应用,通过实例详细讲解了如何利用该算法解决求最长和小于等于K及最短和大于等于K的子数组问题,有效降低算法复杂度。
1887

被折叠的 条评论
为什么被折叠?



