
class Solution {
public int totalFruit(int[] f) {
Map<Integer, Integer> hash = new HashMap<>();
int ret=0;
for(int left =0,right=0;right<f.length;right++) {
int in=f[right];
hash.put(in,hash.getOrDefault(in,0)+1);
while(hash.size()>2) {
int out=f[left];
hash.put(out,hash.get(out)-1);
if(hash.get(out)==0) {
hash.remove(out);
}
left++;
}
ret=Math.max(ret,right-left+1);
}
return ret;
}
}
要解决"滑动窗口(水果成篮)"问题(对应在数组中找最多包含2种不同元素的最长连续子数组长度),可通过「滑动窗口 + 哈希表」思路高效求解,以下是分步讲解:
步骤1:明确问题本质
给定整数数组 fruits(每个元素代表一种水果),仅有2个篮子(即子数组中最多包含2种不同水果 ),需找到满足条件的最长连续子数组长度。
步骤2:暴力法思路(辅助理解,非最优)
枚举所有可能的连续子数组,统计子数组内不同水果的数量:
-
若不同水果数 ≤ 2,更新"最长长度";
-
时间复杂度 O(n2)(n为数组长度),当 n较大时效率极低,因此需优化。
步骤3:滑动窗口 + 哈希表(最优解,时间复杂度 O(n))
通过双指针(左指针 left、右指针 right) 维护"当前窗口",用哈希表记录窗口内各水果的出现次数,确保窗口内始终最多有2种水果。具体流程:
-
初始化变量:
-
左指针
left = 0; -
结果变量
maxLen = 0(记录最长子数组长度); -
哈希表
hash(键为水果类型,值为该类型在窗口内的出现次数)。
-
-
右指针遍历数组:
右指针
right从0遍历到数组末尾,对每个fruits[right]执行:- 将
fruits[right]加入哈希表:若已存在则计数+1,否则初始化为1。
- 将
-
收缩左指针(保证窗口内≤2种水果):
当哈希表的大小(即不同水果的数量)> 2时,需移动左指针缩小窗口:
-
取出
fruits[left]在哈希表中的计数,减1; -
若计数减为0,从哈希表中删除该水果(避免干扰后续判断);
-
左指针
left++,继续检查哈希表大小是否仍>2,直到≤2为止。
-
-
更新最长长度:
每次右指针移动后,计算当前窗口长度
right - left + 1,并与maxLen比较,取较大值更新maxLen。 -
返回结果:
遍历结束后,
maxLen即为满足条件的最长子数组长度。
代码逻辑参考(以Java为例)
public int totalFruit(int[] fruits) {
Map<Integer, Integer> hash = new HashMap<>();
int left = 0, maxLen = 0;
for (int right = 0; right < fruits.length; right++) {
// 右指针加入当前水果,更新哈希表计数
int curFruit = fruits[right];
hash.put(curFruit, hash.getOrDefault(curFruit, 0) + 1);
// 若哈希表大小>2,收缩左指针
while (hash.size() > 2) {
int leftFruit = fruits[left];
// 左指针水果计数减1
hash.put(leftFruit, hash.get(leftFruit) - 1);
// 若计数为0,从哈希表删除
if (hash.get(leftFruit) == 0) {
hash.remove(leftFruit);
}
left++; // 左指针右移
}
// 更新最长子数组长度
maxLen = Math.max(maxLen, right - left + 1);
}
return maxLen;
}
核心思想总结
滑动窗口通过双指针动态调整窗口范围,哈希表高效维护窗口内元素的"种类与数量",确保算法在 O(n)时间复杂度内完成(每个元素仅被左右指针各访问一次),是处理"子数组最多包含K种元素"类问题的通用高效方法。