现有一个正整数数组, 要求从该数组中, 找出一个总和为目标值的连续子数组, 输出该子数组的最大长度
技术要点:
利用滑动窗口的逻辑来解释
利用双指针, 一个指针指向子数组的起始位置, 命名为 start, 另一个指针指向子数组的结束位置,命名为 end. 由于子数组是连续的, 因此可以通过移动指针来改变子数组的大小.
因此在有的地方, 称这种类似的方法为滑动窗口. 实际上准确的说法应该是队列
遍历数组的过程中, 我们让 end 指针不断的向右移动, 每次移动 end 指针时, 都要计算 start 到 end 之间的元素之和.
当 start 到 end 之间的元素之和等于目标值 k 时, 记录当前子数组的长度
如果 start 到 end 之间的元素之和大于目标值 k, 则表示需要减少元素以减少总和, 此时则需要将 start 指针向右移动一位, 直到 start 到 end 之间的元素之和小于等于目标值 k.
代码如下所示
01function maxSubarrayLength(arr, k) {02let sum = 0;03let maxLen = 0;04let start = 0; // 子数组的起始位置0506// 定义子数组的结束位置, 并不断地向右移动07for (let end = 0; end < arr.length; end++) {08// 每移动一次, 计算子数组的和09sum += arr[end];1011// 当子数组的和大于目标值时, 需要移动子数组的起始位置,以减小子数组的和12while (sum > k && start <= end) {13sum -= arr[start];14start++;15}1617// 当子数组的和等于目标值时, 记录当前子数组的长度18if (sum === k) {19maxLen = Math.max(maxLen, end - start + 1);20}21}2223return maxLen;24}
第二种思路是利用空间换时间的方式, 可以得到更快的执行时间
首先, 我们依然定义两个指针 start 和 end, 分别指向子数组的起始位置和结束位置. end 指针不断向右递增移动.
在移动 end 指针的过程中, 我们需要记录从数组的第一个元素到 end 之间的元素总和 sum, 并将其存储在一个哈希表中. 哈希表的键为元素总和, 值为该元素总和对应的下标.
由于子元素的总和是已知的, 因此我们可以通过判断哈希表中是否存在 sum - k 的键来确定是否存在一个子数组的总和等于 k. 公式如下图所示:

如果存在, 我们可以通过计算 end - start 来得到当前子数组的长度, 并将其与之前记录的最大子数组长度进行比较, 取较大值作为新的最大子数组长度.
01function fn(arr, k) {02let maxLen = 0;03let sum = 0;04const map = new Map();05map.set(0, -1); // 初始化哈希表, 将 (0, -1) 键值对添加到哈希表中 (sum, index)0607for (let end = 0; end < arr.length; end++) {08sum += arr[end];09if (map.has(sum - k)) {10const start = map.get(sum - k);11maxLen = Math.max(maxLen, end - start);12}13if (!map.has(sum)) {14map.set(sum, end);15}16}17return maxLen;18}