最近有小伙伴说,面试时面试官问了两道很类似的题目。
只想到第一道题目是关于 滑动窗口 的,第二道就想不出来了。
之前的文章我们就练习过一道关于 「滑动窗口」 的题目:窗口内的最大值 。
还没看过的小伙伴可以点击链接阅读哦~
今天我们就来讨论一下这 两道类似的题目 ,小伙伴们可以进行对比学习!!!
问题 1
给定一个由正整数组成的无序数组 arr 和一个整数 K ,在所有 arr 子数组中找到累加和等于 K ,并且长度最长的子数组,返回该长度。
示例 :
输入: arr = [3,2,1,3,3,1,1,1,1,1,1,2,2,2] ,K = 6
输出: 6
解释: 组合方式有多种:
- 3 + 2 + 1 = 6
- 2 + 1 + 3 = 6
- 3 + 3 = 6
- 3 + 1 + 1 + 1 = 6
- 1 + 1 + 1 + 1 + 1 + 1 = 6
- …
最长子数组为 6 个 1 时,因此返回 6 。
思路分析
由于数组均由正整数构成,因此就隐藏了一个重要的 单调信息 :
- 窗口增大时,窗口累加和一定增大;
- 窗口减小时,窗口累加和一定减小。
根据滑动窗口的思想,构造窗口[L, R]
,初始时[0, 0]
表示窗口内只包含第一个元素 arr[0]
,设置窗口内的累加和变量sum
,即初始值为sum=arr[0]
。
- 若
sum < K
,说明窗口值太小了,需要扩大窗口大小,因此R++
; - 若
sum > K
,说明窗口值太大了,需要缩小窗口大小,因此L++
; - 若
sum = K
,即找到了一组满足要求的子数组,记录并更新此时长度的最大值。缩小或扩大窗口,继续往后寻找是否还有满足要求的子数组。
注意: 要保证
R++
时不会越界哦~
有小伙伴可能会有疑惑:这种方法能够做到 不重不漏 的计算么?
我们对这三种情况都进行下分析:
- 当
sum < K
时,R++
而L
不变,相当于在寻找以L
为起始点的子数组中是否有满足要求的。(由于具有单调性,满足要求的子数组只可能比此时的长度长,因此R++
)。- 当
sum > K
时,L++
而R
不变,相当于以L
为起始点的子数组中一定不会出现满足要求的了。(因此L++
缩小范围往后继续寻找)。- 当
sum = K
时,记录此时长度。如果选择R++
,那下一次一定会触发情况 2 ;如果选择L++
,那下一次一定会触发情况 1 (具体实现选择一种即可)。实现了往后继续寻找的功能。
代码
public static int getMaxLength(int[] arr, int K) {
if (arr == null || arr.length == 0 || K <= 0) {
return 0;
}
int left = 0;
int right = 0;
int sum = arr[0];
int len = 0;
// 不越界
while (right < arr.length) {
if (sum == K) {
// 更新长度信息
len = Math.max(len, right - left + 1);
// 选择了移动左侧
sum -= arr[left++];
} else if (sum < K) {
// 已经到达最后一个位置了
right++;
if (right == arr.length) {
break;
}
sum += arr[right];
} else {
// sum > K 时 L++
sum -= arr[left++];
}
}
return len;
}
思考一下
理解了 窗口功能 的思路之后,写出代码还是轻而易举的!
小伙伴们思考两个问题:
- 代码中的
sum
初始值设置成了arr[0]
。如果修改成初始值:sum = 0
,其他部分代码应该如何修改呢?含义是什么? - 代码中的
sum == K
时,选择了L++
,即缩小窗口。如果选择R++
扩大窗口,代码如何写呢?(注意边界判断~)
评论区讨论一下吧~
对于第二个上难度的问题,小伙伴们先自己思考讨论一下吧!我们下篇文章接着聊~
关注
回复「ACM紫书」获取 ACM 算法书籍 ~
回复「算法导论」获取 算法导论第3版 ~
在看 + 转发
让你的小伙伴们一起来学算法吧!!