一、问题描述
水果成篮
说明:题目来自leetcode
Category | Difficulty | Likes | Dislikes |
---|---|---|---|
algorithms | Medium (44.44%) | 165 | - |
你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits
表示,其中 fruits[i]
是第 i
棵树上的水果 种类 。
你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:
- 你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
- 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
- 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。
给你一个整数数组 fruits
,返回你可以收集的水果的 最大 数目。
示例 1:
输入:fruits = [1,2,1]
输出:3
解释:可以采摘全部 3 棵树。
示例 2:
输入:fruits = [0,1,2,2]
输出:3
解释:可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。
示例 3:
输入:fruits = [1,2,3,2,2]
输出:4
解释:可以采摘 [2,3,2,2] 这四棵树。
如果从第一棵树开始采摘,则只能采摘 [1,2] 这两棵树。
示例 4:
输入:fruits = [3,3,3,1,2,1,1,2,3,3,4]
输出:5
解释:可以采摘 [1,2,1,1,2] 这五棵树。
提示:
1 <= fruits.length <= 105
0 <= fruits[i] < fruits.length
二、思路
该题为滑动窗口问题,涉及多指针,特色可能是通过数组来保存篮子采摘水果种类。
简介 :
-
先使用两个变量作为两个篮子,主要是记录果树种类;
-
先使用right指针遍历数组,如果此时两个篮子都已经装有水果,并且该right指针对应的水果的种类不属于篮子中水果的种类,于是:
-
1、计算已经采集的果树的棵树,并使用变量count记录;
-
2、对左指针(left)重置;
-
3、对篮子所对应的种类重置;
-
前提引入:
-
计划是使用区间为[left,right-1]来记录两种果树的最长范围,于是后面就用
((right-1)- left + 1)
=(right - left)
来记录此时子数组的长度; -
使用左指针left来表示子果树数组的左端点;
-
设置整型数组kind(大小为2)来表示篮子的种类情况,初始值均为-1,代表还没有确认水果的种类;当确认了水果种类对数组的数值修改为水果种类;
xin麒的思路分析(具体实现):
先定义数组kind来记录水果篮要装的水果的种类、int变量count是记录子数组的长度、int型变量1left作为水果子数组左指针、int型变量note作为判断子数组是否等于整个数组(后面会说);
- 1.1、首先kind没有一个元素初始值为-1,在循环里面当遇到第一个元素时先判断第一个篮子是否为-1:如果是-1,表示还没有确认,于是将该**fruit[right]赋值给kind[0]**表示确认了一种水果类型,于是继续果农探索下一棵果树;
1.2、继续循环时,如果再次遇到和**kind[0]**类型相同的水果时,那么可以采摘,于是继续果农探索下一棵果树;
1.3、继续循环时,如果**fruit[right]和kind[0]不相等,并且kind[1]**等于-1,说明遇到新的水果种类,同时第二个篮子还没有确认水果的种类,那么就让第二个篮子确定种类
kind[1] = fruit[right]
,于是继续果农探索下一棵果树;
- 1.4、继续循环时如果遇到和篮子**kind[1]**确认的水果种类,那么说明可以采摘,于是继续果农探索下一棵果树;
- 2.1、当遇到第三种水果时(此时fruit[right]不属于两个篮子的认定的水果种类 ),那么就需要记录一下之前两种水果树的采摘数量了,结果为
((right-1)- left + 1)
=(right - left)
;先是和count比较,如果count小,才可以将(right - left)
赋值给count;
- 2.2、于是对left进行处理,为了实现将当前水果的子数组的水果种类处理为1种,而且另一种新的水果种类为**
fruit[right]
,于是我们需要:将fruit[right - 1]作为第一个水果篮子的确定的水果类型、将fruit[right]**作为第二个水果篮子的确定的水果类型;注意:这里的选取尤为重要,决定了left是从子数组的右端还是左端开始遍历
- 2.3、上述为了实现上述需要,我们取一个变量start = right-2 作为指针,从fruit[start]向左遍历,当
fruits[right - 1] == fruits[start]
不成立时,便停下,说明fruit[left]为之前kind[0]类型的水果树,于是退出循环;于是left = start + 1
;同时将**fruit[right-1]赋值于kind[0]篮子,将fruit[right]赋值于kind[1]**篮子;
- 3.1、考虑到如果没有进入循环,那么note的数字始终不变(初始值设置为**-2**,也可以设置为其他,反正都是为了标记),那么说明该果园仅仅2种水果,并且count就没有被改变(也就是依然为初始值0);于是我们可以判断note是否为**-2**,如果是直接返回所有果树组成的长度;
- 3.2、还有一种情况没有被统计,就是最后一次循环得到的子数组,循环退出时,索引在区间**[fruit.length - 1,left]的果树没有条件,于是将((fruits.length - 1) - left + 1)**与count比较一下,返回其中大的值;
三、xin麒的题解:
class Solution {
public int totalFruit(int[] fruits) {
int[] kind = {-1,-1};
int count = 0;
int left = 0;
int note = -2;
for (int right = 0; right < fruits.length; right++) {
if (kind[0] == -1){
kind[0] = fruits[right];
continue;
}
if (kind[0] == fruits[right]){
continue;
}
if (kind[1] == -1){
kind[1] = fruits[right];
continue;
}
if (kind[1] == fruits[right]){
continue;
}
note = 0;
int number = right - left;
if (number > count){
count = number;
}
int start = right - 2;
while (fruits[right - 1] == fruits[start]){
start--;
}
left = start + 1;
kind[0] = fruits[right -1];
kind[1] = fruits[right];
}
if(note == -2){
return fruits.length;
}
if(fruits.length - left> count){return (fruits.length - left);}
return count;
}
}