1、题目描述
你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits
表示,其中 fruits[i]
是第 i
棵树上的水果 种类 。
你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:
- 你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
- 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
- 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。
给你一个整数数组 fruits
,返回你可以收集的水果的 最大 数目。
2、算法思路
研究的对象是⼀段连续的区间,可以使⽤「滑动窗⼝」思想来解决问题。
让滑动窗⼝满⾜:窗⼝内⽔果的种类只有两种。 做法:右端⽔果进⼊窗⼝的时候,⽤哈希表统计这个⽔果的频次。这个⽔果进来后,判断哈希表的 ⼤⼩: 如果⼤⼩超过2:说明窗⼝内⽔果种类超过了两种。那么就从左侧开始依次将⽔果划出窗 ⼝,直到哈希表的⼤⼩⼩于等于2,然后更新结果; 如果没有超过2,说明当前窗⼝内⽔果的种类不超过两种,直接更新结果ret。
可以使用数组来代替真哈希表,速度更快。而且上述的条件指出我们可以知道数组中的数字的大小和数字的长度有关。
3、算法流程
1、进窗口
每个元素进入滑动窗口
2、判断
如果窗口内的水果总类大于2
出窗口(水果总类大于2)
左侧元素滑出窗口,并且该元素的所在数组的位置大小减一,并left移动一位,当所在的left元素对应的索引的值大小减少到0,,该元素从哈希表中删除,水果种类减少。
3、更新结果
更新最大的ret
4、算法代码
使用数组代替哈希表
class Solution {
public int totalFruit(int[] fruits) {
int n = fruits.length;
if (n <= 2) {
return n;
}
int[] hash = new int[n+1];//统计窗口内水果的种类
int ret = 0;
for (int left = 0,right = 0,kinds = 0;right < n;right++){
int into = fruits[right];
if (hash[into] == 0) kinds++;//统计水果的总类
hash[into]++;//进窗口
while (kinds > 2){//判断
int out = fruits[left];
hash[out]--;//目的的减去连续一样的数
left++;//出窗口
if (hash[out] == 0) kinds--;//直到连续一样数减到0,水果总类才减少
}
ret = Math.max(ret,right - left +1);
}
return ret;
}
}
使用哈希表
class Solution {
public int totalFruit(int[] fruits) {
int n = fruits.length;
if (n <= 2) {
return n ;
}
Map<Integer, Integer> hash = new HashMap<Integer, Integer>();
int left = 0, ret = 0;
for (int right = 0; right < n; right++) {
int in = fruits[right];
hash.put(in, hash.getOrDefault(in, 0) + 1);//进窗口
while (hash.size() > 2) {//判断
int out = fruits[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;
}
}