滑动窗口
在解决连续数组类问题时,滑动窗口是一个很好的解法,下面以一个例子进行说明
长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 target.
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度. 如果不存在符合条件的子数组,返回 0 。
输入: target = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的子数组。
- 一开始,滑动窗口只有2一个元素,sum小于target,右端点不断向右扩展
- 滑动窗口扩展到2,3,1,2时,sum大于target,此时左端点收缩,更新最小连续子数组长度
- 收缩后,sum小于target,右端点向右扩展
- ...
- 直到滑动窗口元素只有4,3时,得到最小连续子数组长度
function minSubArrayLen(target: number, nums: number[]): number {
let res: number = Infinity
let left = 0,
right = 0,
sum = nums[left]
while (left <= right && right < nums.length) {
if (sum >= target) {
res = Math.min(res, right - left + 1)
sum -= nums[left]
left++
} else {
right++
sum += nums[right]
}
}
if (left === 0 && right === nums.length) return 0
return res
}
滑动窗口写法模板
function minSubArrayLen(target: number, nums: number[]): number {
let res: number = Infinity
let left = 0, sum = 0
for (let right = 0; right < nums.length; right++) {
// 更新右端点
sum += nums[right]
// 更新左端点
while(sum >= target) {
sum -= nums[left]
res = Math.min(res, right - left + 1)
left++
}
}
if(res > nums.length) res = 0
return res
}
乘积小于k的子数组
给你一个整数数组 nums 和一个整数 k ,请你返回子数组内所有元素的乘积严格小于 k 的连续子数组的数目。
输入: nums = [10,5,2,6], k = 100
输出: 8
解释: 8 个乘积小于 100 的子数组分别为:[10]、[5]、[2],、[6]、[10,5]、[5,2]、[2,6]、[5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于 100 的子数组。
function numSubarrayProductLessThanK(nums: number[], k: number): number {
let res: number = 0,
prod = 1,
left = 0
for (let right = 0; right < nums.length; right++) {
if (k <= 1) return 0
// 更新右端点
prod *= nums[right]
// 更新左端点
while (prod >= k) {
prod /= nums[left]
left++
}
// left, left + 1, ..., right 相乘均小于k
// 那么以right结尾的子数组个数为 right - left + 1
res += right - left + 1
}
return res
}
无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
function lengthOfLongestSubstring(s: string): number {
if (s.length === 0) return 0
let res = 0,
left = 0,
cache = new Map() // 记录出现过的字符个数
for (let right = 0; right < s.length; right++) {
const num = cache.get(s[right]) || 0
// 更新右端点
cache.set(s[right], num + 1)
// 更新左端点
while (cache.get(s[right]) > 1) {
cache.set(s[left], cache.get(s[left]) - 1)
left++
}
res = Math.max(res, right - left + 1)
}
return res
}