LeetCode 904
水果成篮
在一排树中,第i
棵树产生tree[i]
型的水果。
你可以从你选择的任何树开始,然后重复执行以下步骤:
- 把这棵树上的水果放进你的篮子里。如果你做不到,就停下来。
- 移动到当前树右侧的下一棵树。如果右边没有树,就停下来。
请注意,在选择一颗树后,你没有任何选择:你必须执行步骤 1,然后执行步骤 2,然后返回步骤 1 ,然后执行步骤 2,依此类推,直至停止。
你有两个篮子,每个篮子可以携带任何数量的水果,但你希望每个篮子只携带一种类型的水果。
用这个程序你能收集的水果总量是多少?
解法1:两层for循环
解题思路:
从一个起点开始,遍历后面的元素,如果有一个元素既不等于起点又不等于前面不等于起点的元素,我们就停止遍历
代码如下:
class Solution {
public int totalFruit(int[] tree) {
int res = 0;
int i, j;
for(i=0; i<tree.length; i++)
{
int pre=0; //用pre来保存前面一个元素跟起点的差
for(j=i+1; j<tree.length; j++)
{
if(tree[j]-tree[i]!=0 && tree[j]-tree[i]!=pre)
{
if(pre==0) //如果前面一个元素跟起点相同
pre=tree[j]-tree[i]; //则当前元素为第二个元素
else
//如果当前元素跟起点的差不等于前面一个元素跟起点的差,说明两者不相等,当前元素为第三个元素,退出循环
break;
}
}
res = Math.max(res,j-i); //保存结果
}
return res;
}
}
时间复杂度为O(N^2)
空间复杂度为O(1)
解法2:按块扫描
解题思路:
我们把一段连续的数字称为块,我们用一个block数组来保存每一个块的首地址,如
tree = [(1,1),(2,2),(1,1),(2),(3,3,3)] //括号表示一块连续数字
block = [0,2,4,6,7] //分别代表每一块的首地址
我们把一个块里面的值称为块元素
那么我们的任务就是找到由两种块元素组成的最大长度的块,而且这两种类型元素的块必须是相邻的
注意:我们遍历block数组时,当找完两种元素的块,只要从前面一个块重新开始找就可以了,
如果从前面两个块开始找,那就没啥意义了,因为已经存在两种元素了,没办法继续往下找
代码如下:
class Solution {
public int totalFruit(int[] tree) {
List<Integer> blockLefts = new ArrayList();
for (int i = 0; i < tree.length; ++i)//存放每一块的首地址
if (i == 0 || tree[i-1] != tree[i])
blockLefts.add(i);
// 代表块的结尾
blockLefts.add(tree.length);
int ans = 0, i = 0;
search: while (true) {
//用来保存每一个块元素
Set<Integer> types = new HashSet();
int weight = 0; //组合块的长度
遍历块
for (int j = i; j < blockLefts.size() - 1; ++j) {
// 加入块元素
types.add(tree[blockLefts.get(j)]);
weight += blockLefts.get(j+1) - blockLefts.get(j);
//下一个块的首地址减去当其块的首地址等于当前块的长度
// 如果有三种块元素
if (types.size() >= 3) {
i = j - 1; //返回到上一个块开始遍历
continue search;
}
//保存组合块的最大长度
ans = Math.max(ans, weight);
}
break;
}
return ans;
}
}
时间复杂度为O(N)
空间复杂度为O(N)
解法3:滑动窗口
解题思路:
下标:[0,1,2,3,4,5,6,7,8,9]
数值:[1,1,6,5,6,6,1,1,1,1]
从左往右先找到2种树,即(1, 6)
。
当end下标为3时,遇到了第3种树(5)
,而第3种树(5)
左边的树是6,将2种树的状态更新为(6,5)
。
以此类推,当下标为6时,遇到了第3种树(1)
,而第3种树(1)
左边的树是6,将2种树的状态更新为(6,1)
。
即每次2种树的状态更新为(第3种树的左边的树, 第3种树)
。
当理解上述思路后,就开始计数由这2种树所构成的连续子数组。
(1, 6) -> [1, 1, 6] ->
连续子数组长度3
(6, 5) -> [6, 5, 6, 6] ->
连续子数组长度为4
(6, 1) -> [6, 6, 1, 1, 1, 1] ->
连续子数组长度为6
取连续子数组的最长长度作为返回结果,也就是6
。
代码如下:
class Solution {
public int totalFruit(int[] tree) {
int res = 0, len = tree.length;
int one = tree[0], two, begin = 0, end = 1;
//2种树的状态(one, two), one初始化为tree数组的第1个元素
while (end < len && tree[end] == one)
//寻找two的初始值,以构成初始(one, two)
++end;
if (end >= len-1) return len;
//若整个数组的元素都由初始的(one, two)所构成,则直接返回数组长度
two = tree[end++]; //构成初始的(one, two)
for (; end < len; ++end) {
if (one != tree[end] && two != tree[end]) {
//遇到了第3种树
res = Math.max(res, end - begin);
//更新最终返回结果
one = tree[end - 1];
//(one, two)更新为(第3种树的左边的树, 第3种树)
two = tree[end];
begin = end - 1;
//更新由当前(one, two)所构成的连续子数组的左边界
while (tree[begin - 1] == one)
//向左寻找由one构成的连续子数组
--begin;
}
}
return Math.max(res, end - begin);
}
}
时间复杂度为O(N)
空间复杂度为O(1)
解法3来源:
作者:gfu
链接:https://leetcode-cn.com/problems/fruit-into-baskets/solution/java-hua-dong-chuang-kou-5ms-beat-100-by-gfu/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。