【Leetcode】904.Fruit Into Baskets
Sliding Window array hashmap set Medium
You are visiting a farm that has a single row of fruit trees arranged from left to right. The trees are represented by an integer array fruits where fruits[i] is the type of fruit the ith tree produces.
You want to collect as much fruit as possible. However, the owner has some strict rules that you must follow:
You only have two baskets, and each basket can only hold a single type of fruit. There is no limit on the amount of fruit each basket can hold.
Starting from any tree of your choice, you must pick exactly one fruit from every tree (including the start tree) while moving to the right. The picked fruits must fit in one of your baskets.
Once you reach a tree with fruit that cannot fit in your baskets, you must stop.
Given the integer array fruits, return the maximum number of fruits you can pick.
Input: fruits = [1,2,3,2,2]
Output: 4
Explanation: We can pick from trees [2,3,2,2].
If we had started at the first tree, we would only pick from trees [1,2].
Intuition
求至多含两种不同元素的子序列的最长长度。
显然是一道滑动窗口题。
暴力循环(即使用上早停)可能过不了。
方法一:Optimized Brute Force
预备知识
可以用set 型变量统计字串元素种类数。
# python
basket = set() # 新建set
basket.add(fruits[current_index]) # 添加元素
len(basket) # set是有序不重复的集合。
// C++
set<int> basket;
basket.insert(fruits[current_index]);
basket.size();
思路及算法
- 初始化max_picked = 0;
- 遍历左指针;
- 对每个左指针,遍历右指针,同时统计当前字串的元素种数,如果超过2,退出右指针的循环,左指针+1;否则更新max_picked。
代码
//C++
class Solution {
public:
int totalFruit(vector<int>& fruits) {
int max_picked = 0;
for(int i = 0; i < fruits.size(); i++){
set<int> basket;
int right = left;
while(right < fruits.size()){
if(basket.find(fruits[right]) == basket.end() && basket.size() == 2)
break;
basket.insert(fruits[right]);
right++;
}
max_picked = max(max_picked, right-left);
}
return max_picked;
}
};
class Solution:
def totalFruit(self, fruits: List[int]) -> int:
# Maximum number of fruits we can pick
max_picked = 0
# Iterate over the left index left of subarrays.
for left in range(len(fruits)):
# Empty set to count the type of fruits.
basket = set()
right = left
# Iterate over the right index right of subarrays.
while right < len(fruits):
# Early stop. If adding this fruit makes 3 types of fruit,
# we should stop the inner loop.
if fruits[right] not in basket and len(basket) == 2:
break
# Otherwise, update the number of this fruit.
basket.add(fruits[right])
right += 1
# Update max_picked
max_picked = max(max_picked, right - left)
# Return maxPicked as the maximum length of valid subarray.
# (maximum number of fruits we can pick).
return max_picked
复杂度分析
Time complexity:
O
(
n
2
)
O(n^2)
O(n2)
Space complexity:
O
(
1
)
O(1)
O(1)
方法二: 滑窗1
思路及算法
始终保持滑窗内只有两种以下的元素。
- 初始化左右指针p、q均指向第一个元素;当前滑窗内种类数为1,countpick=0;
- 如果序列长度短于2,那么countpick=序列长度;
- 移动右指针,直到移动到子序列恰好包含两个元素或序列最后一个位置;
- 记录此时子序列的长度,更新countpick;
- 移动左指针,使子序列恰好只包含一个元素;
- 重置滑窗内种类数。
代码
class Solution {
public:
int totalFruit(vector<int>& fruits) {
int p = 0, q = 0;
int type1, type2;
int counttype = 1, countpick=0;
if(fruits.size() <= 2)
return fruits.size();
while(p<fruits.size()){
type1=fruits[p];
while(q < fruits.size()-1){
q++;
if(counttype == 2 && (fruits[q] != type1 && fruits[q] != type2)){
q--;
break;
}
if(fruits[q] != type1 && counttype == 1){
type2 = fruits[q];
counttype = 2;
}
else
continue;
}
countpick = countpick > (q-p+1) ? countpick : (q-p+1);
p=q;
while(p > 0 && fruits[p] == fruits[q])
p--;
p=p+1;
counttype = 1;
if(q == fruits.size()-1)
break;
}
return countpick;
}
};
复杂度分析
Time complexity:
O
(
n
)
O(n)
O(n)
Space complexity:
O
(
1
)
O(1)
O(1)
踩坑记录
- 左指针移动后的位置应从q的位置向左找,如果从左往右找不能保证子序列只含一种元素。
- 特别容易越界。在VSCODE上没报错,LeetCode就会报错。只能在每次要引用前加一个限制条件。。。
方法三:滑窗2(Map)
预备知识
映射<key=type, value=某种水果的数量>
//C
unordered_map<int, int> basket; //新建一个映射map
basket[x]; //key=x的value+1,如果map里找不到键x,自动新增键x,键值为一。
basket.erase(x); //删除键x
# python
basket = {} # 新建一个字典
basket[x]; # key=x的value+1,如果map里找不到键x,自动新增键x
basket.get(x, 0)# 返回指定键x的值,如果键不在字典中返回设置的默认值0
del basket[x] # 删除键x
思路与算法
- 用hash map 记录当前窗口的水果种类
- 移动右指针,如果当前窗口水果种类>2,移动左指针直到窗口内只有两种水果;否则更新 max_picked = max(max_picked, right - left + 1)
代码
//c++
class Solution {
public:
int totalFruit(vector<int>& fruits) {
// We use a hash map 'basket' to store the number of each type of fruit.
unordered_map<int, int> basket;
int left = 0, maxPicked = 0;
// Add fruit from the right index (right) of the window.
for (int right = 0; right < fruits.size(); ++right) {
basket[fruits[right]]++;
// If the current window has more than 2 types of fruit,
// we remove fruit from the left index (left) of the window,
// until the window has only 2 types of fruit.
while (basket.size() > 2) {
basket[fruits[left]]--;
if (basket[fruits[left]] == 0)
basket.erase(fruits[left]);
left++;
}
// Update maxPicked.
maxPicked = max(maxPicked, right - left + 1);
}
// Return maxPicked as the maximum number of fruits we can collect.
return maxPicked;
}
};
# python
class Solution:
def totalFruit(self, fruits: List[int]) -> int:
# We use a hash map 'basket' to store the number of each type of fruit.
basket = {}
max_picked = 0
left = 0
# Add fruit from the right index (right) of the window.
for right in range(len(fruits)):
basket[fruits[right]] = basket.get(fruits[right], 0) + 1
# If the current window has more than 2 types of fruit,
# we remove fruit from the left index (left) of the window,
# until the window has only 2 types of fruit.
while len(basket) > 2:
basket[fruits[left]] -= 1
if basket[fruits[left]] == 0:
del basket[fruits[left]]
left += 1
# Update max_picked.
max_picked = max(max_picked, right - left + 1)
# Return max_picked as the maximum number of fruits we can collect.
return max_picked
复杂度分析
Time complexity:
O
(
n
)
O(n)
O(n)
Space complexity:
O
(
1
)
O(1)
O(1)
方法四:滑窗(固定窗口大小)
思路和算法
- 每次迭代右指针+1,检查当前左右指针间的子序列是否符合要求,如果不符合,左指针+1,否则右指针继续移动。
- 右指针完成迭代后,result=right - left +
代码
//c++
class Solution {
public:
int totalFruit(vector<int>& fruits) {
// Hash map 'basket' to store the types of fruits.
unordered_map<int, int> basket;
int left, right;
// Add fruit from the right index (right) of the window.
for (left = 0, right = 0; right < fruits.size(); ++right) {
basket[fruits[right]]++;
// If the current window has more than 2 types of fruit,
// we remove one fruit from the left index (left) of the window.
if (basket.size() > 2) {
basket[fruits[left]]--;
if (basket[fruits[left]] == 0)
basket.erase(fruits[left]);
left++;
}
}
// Once we finish the iteration, the indexes left and right
// stands for the longest valid subarray we encountered.
return right - left;
}
};
#python
class Solution:
def totalFruit(self, fruits: List[int]) -> int:
# Hash map 'basket' to store the types of fruits.
basket = {}
left = 0
# Add fruit from the right index (right) of the window.
for right, fruit in enumerate(fruits):
basket[fruit] = basket.get(fruit, 0) + 1
# If the current window has more than 2 types of fruit,
# we remove one fruit from the left index (left) of the window.
if len(basket) > 2:
basket[fruits[left]] -= 1
# If the number of fruits[left] is 0, remove it from the basket.
if basket[fruits[left]] == 0:
del basket[fruits[left]]
left += 1
# Once we finish the iteration, the indexes left and right
# stands for the longest valid subarray we encountered.
return right - left + 1
方法五:单循环
思路和算法
花了好大劲才看明白, 算法和实现都很简练。
- size: valid子序列长度;res: max(res, size) 最终结果;same_count:连续元素的个数;a:离列尾更远的子序列元素种数;b:子序列尾的元素种类;
- 遍历序列中的每个元素:
- 如果与a相同, size+1, same_count重新计数,交换a, b
- 如果与b相同,连击! same_count+1,size+1;
- 如果与a、b都不同,size为当前连击的个数+1,same_count重新计数,a=b,b=新出现(子序列的第三种)元素。
代码
class Solution {
public:
int totalFruit(vector<int>& tree) {
int n=tree.size();
int size=0, same_count=0, a=0, b=0, cur=0;
int res=0; // final answer
for(int i=0;i<n;i++){
int cur=tree[i];
if(cur==b){
size++;
same_count++;
}
else if(cur==a){
size++;
same_count=1;
a=b;
b=cur;
}
else{
size=same_count+1;
same_count=1;
a=b;
b=cur;
}
res=max(res,size);
}
return res;
}
};
复杂度分析
Time complexity:
O
(
n
)
O(n)
O(n)
Space complexity:
O
(
1
)
O(1)
O(1)