Skip to content

Commit 4b0cd9f

Browse files
Add Fruits Into Baskets II solution and update README
1 parent eb950db commit 4b0cd9f

File tree

5 files changed

+310
-2
lines changed

5 files changed

+310
-2
lines changed
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
# Fruits Into Baskets II - Problem #2107
2+
3+
## Problem Statement
4+
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 `i`th tree produces.
5+
6+
You want to collect as much fruit as possible. However, the owner has some strict rules that you must follow:
7+
8+
- 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.
9+
- Starting from any tree, 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.
10+
- Once you reach a tree with fruit that cannot fit in your baskets, you must stop.
11+
- You can **skip at most one tree** during your collection.
12+
13+
Given the integer array `fruits`, return the **maximum number of fruits** you can pick.
14+
15+
## Examples
16+
```
17+
Input: fruits = [1,2,1]
18+
Output: 3
19+
Explanation: We can pick from all 3 trees.
20+
21+
Input: fruits = [0,1,2,2]
22+
Output: 4
23+
Explanation: We can pick from trees [0,1,2,2] by skipping the tree at index 2.
24+
25+
Input: fruits = [1,2,3,2,2]
26+
Output: 5
27+
Explanation: We can pick from trees [1,2,3,2,2] by skipping the tree at index 2.
28+
```
29+
30+
## Approach
31+
**Key Insight**: This is an extension of the classic sliding window problem where we need to find the longest subarray containing at most 2 different types of fruits, but with the ability to skip at most one tree.
32+
33+
**Algorithm**:
34+
1. Use a sliding window with two pointers (left and right).
35+
2. Use a HashMap to track the count of each fruit type in the current window.
36+
3. Expand the window by moving the right pointer and add fruits to the map.
37+
4. When the window contains more than 2 fruit types, we can skip at most one tree.
38+
5. Keep track of the maximum window size found.
39+
40+
**Why this works**:
41+
- We need to find the longest contiguous sequence with at most 2 different fruit types
42+
- The ability to skip one tree allows us to handle cases where we encounter a third fruit type
43+
- Sliding window efficiently maintains the constraint while allowing for the skip
44+
45+
## Complexity Analysis
46+
- **Time Complexity**: O(n) - Each element is visited at most twice (once by right pointer, once by left pointer)
47+
- **Space Complexity**: O(1) - HashMap will contain at most 3 entries (2 types + 1 skipped)
48+
49+
## Key Insights
50+
- This is essentially finding the longest subarray with at most 2 distinct elements, with one skip allowed
51+
- The skip allows us to handle a third fruit type by ignoring it
52+
- We need to track which fruit type we're skipping and when
53+
54+
## Alternative Approaches
55+
1. **Brute Force**: Try all possible subarrays with skips - O(n²) time
56+
2. **Dynamic Programming**: Can be used but overkill for this problem
57+
3. **Two Pointers**: Similar to sliding window but less efficient
58+
59+
## Solutions in Different Languages
60+
61+
### Java
62+
```java
63+
// See solution.java
64+
import java.util.*;
65+
66+
class Solution {
67+
public int totalFruit(int[] fruits) {
68+
Map<Integer, Integer> basket = new HashMap<>();
69+
int left = 0;
70+
int maxFruits = 0;
71+
int skipCount = 0;
72+
73+
for (int right = 0; right < fruits.length; right++) {
74+
basket.put(fruits[right], basket.getOrDefault(fruits[right], 0) + 1);
75+
76+
// If we have more than 2 types, we can skip at most one
77+
while (basket.size() > 2 && skipCount < 1) {
78+
// Skip the leftmost fruit
79+
basket.put(fruits[left], basket.get(fruits[left]) - 1);
80+
if (basket.get(fruits[left]) == 0) {
81+
basket.remove(fruits[left]);
82+
}
83+
left++;
84+
skipCount++;
85+
}
86+
87+
// If we still have more than 2 types, shrink window normally
88+
while (basket.size() > 2) {
89+
basket.put(fruits[left], basket.get(fruits[left]) - 1);
90+
if (basket.get(fruits[left]) == 0) {
91+
basket.remove(fruits[left]);
92+
}
93+
left++;
94+
}
95+
96+
maxFruits = Math.max(maxFruits, right - left + 1);
97+
}
98+
99+
return maxFruits;
100+
}
101+
}
102+
```
103+
104+
### JavaScript
105+
```javascript
106+
// See solution.js
107+
/**
108+
* @param {number[]} fruits
109+
* @return {number}
110+
*/
111+
var totalFruit = function(fruits) {
112+
const basket = new Map();
113+
let left = 0;
114+
let maxFruits = 0;
115+
let skipCount = 0;
116+
117+
for (let right = 0; right < fruits.length; right++) {
118+
basket.set(fruits[right], (basket.get(fruits[right]) || 0) + 1);
119+
120+
// If we have more than 2 types, we can skip at most one
121+
while (basket.size > 2 && skipCount < 1) {
122+
basket.set(fruits[left], basket.get(fruits[left]) - 1);
123+
if (basket.get(fruits[left]) === 0) {
124+
basket.delete(fruits[left]);
125+
}
126+
left++;
127+
skipCount++;
128+
}
129+
130+
// If we still have more than 2 types, shrink window normally
131+
while (basket.size > 2) {
132+
basket.set(fruits[left], basket.get(fruits[left]) - 1);
133+
if (basket.get(fruits[left]) === 0) {
134+
basket.delete(fruits[left]);
135+
}
136+
left++;
137+
}
138+
139+
maxFruits = Math.max(maxFruits, right - left + 1);
140+
}
141+
142+
return maxFruits;
143+
};
144+
```
145+
146+
### Python
147+
```python
148+
# See solution.py
149+
from typing import List
150+
from collections import defaultdict
151+
152+
class Solution:
153+
def totalFruit(self, fruits: List[int]) -> int:
154+
basket = defaultdict(int)
155+
left = 0
156+
max_fruits = 0
157+
skip_count = 0
158+
159+
for right in range(len(fruits)):
160+
basket[fruits[right]] += 1
161+
162+
# If we have more than 2 types, we can skip at most one
163+
while len(basket) > 2 and skip_count < 1:
164+
basket[fruits[left]] -= 1
165+
if basket[fruits[left]] == 0:
166+
del basket[fruits[left]]
167+
left += 1
168+
skip_count += 1
169+
170+
# If we still have more than 2 types, shrink window normally
171+
while len(basket) > 2:
172+
basket[fruits[left]] -= 1
173+
if basket[fruits[left]] == 0:
174+
del basket[fruits[left]]
175+
left += 1
176+
177+
max_fruits = max(max_fruits, right - left + 1)
178+
179+
return max_fruits
180+
```
181+
182+
## Test Cases
183+
```
184+
Test Case 1: [1,2,1] → 3
185+
Test Case 2: [0,1,2,2] → 4
186+
Test Case 3: [1,2,3,2,2] → 5
187+
Test Case 4: [3,3,3,1,2,1,1,2,3,3,4] → 6
188+
Test Case 5: [1,1,1,1] → 4
189+
```
190+
191+
## Edge Cases
192+
- All fruits are the same type
193+
- Only one fruit type
194+
- Empty array
195+
- Large arrays with many different fruit types
196+
- Cases where skipping doesn't help
197+
198+
## Related Problems
199+
- Fruit Into Baskets (original problem)
200+
- Longest Substring Without Repeating Characters
201+
- Longest Substring with At Most K Distinct Characters
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import java.util.*;
2+
3+
class Solution {
4+
public int totalFruit(int[] fruits) {
5+
Map<Integer, Integer> basket = new HashMap<>();
6+
int left = 0;
7+
int maxFruits = 0;
8+
int skipCount = 0;
9+
10+
for (int right = 0; right < fruits.length; right++) {
11+
basket.put(fruits[right], basket.getOrDefault(fruits[right], 0) + 1);
12+
13+
// If we have more than 2 types, we can skip at most one
14+
while (basket.size() > 2 && skipCount < 1) {
15+
// Skip the leftmost fruit
16+
basket.put(fruits[left], basket.get(fruits[left]) - 1);
17+
if (basket.get(fruits[left]) == 0) {
18+
basket.remove(fruits[left]);
19+
}
20+
left++;
21+
skipCount++;
22+
}
23+
24+
// If we still have more than 2 types, shrink window normally
25+
while (basket.size() > 2) {
26+
basket.put(fruits[left], basket.get(fruits[left]) - 1);
27+
if (basket.get(fruits[left]) == 0) {
28+
basket.remove(fruits[left]);
29+
}
30+
left++;
31+
}
32+
33+
maxFruits = Math.max(maxFruits, right - left + 1);
34+
}
35+
36+
return maxFruits;
37+
}
38+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* @param {number[]} fruits
3+
* @return {number}
4+
*/
5+
var totalFruit = function(fruits) {
6+
const basket = new Map();
7+
let left = 0;
8+
let maxFruits = 0;
9+
let skipCount = 0;
10+
11+
for (let right = 0; right < fruits.length; right++) {
12+
basket.set(fruits[right], (basket.get(fruits[right]) || 0) + 1);
13+
14+
// If we have more than 2 types, we can skip at most one
15+
while (basket.size > 2 && skipCount < 1) {
16+
basket.set(fruits[left], basket.get(fruits[left]) - 1);
17+
if (basket.get(fruits[left]) === 0) {
18+
basket.delete(fruits[left]);
19+
}
20+
left++;
21+
skipCount++;
22+
}
23+
24+
// If we still have more than 2 types, shrink window normally
25+
while (basket.size > 2) {
26+
basket.set(fruits[left], basket.get(fruits[left]) - 1);
27+
if (basket.get(fruits[left]) === 0) {
28+
basket.delete(fruits[left]);
29+
}
30+
left++;
31+
}
32+
33+
maxFruits = Math.max(maxFruits, right - left + 1);
34+
}
35+
36+
return maxFruits;
37+
};
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from typing import List
2+
from collections import defaultdict
3+
4+
class Solution:
5+
def totalFruit(self, fruits: List[int]) -> int:
6+
basket = defaultdict(int)
7+
left = 0
8+
max_fruits = 0
9+
skip_count = 0
10+
11+
for right in range(len(fruits)):
12+
basket[fruits[right]] += 1
13+
14+
# If we have more than 2 types, we can skip at most one
15+
while len(basket) > 2 and skip_count < 1:
16+
basket[fruits[left]] -= 1
17+
if basket[fruits[left]] == 0:
18+
del basket[fruits[left]]
19+
left += 1
20+
skip_count += 1
21+
22+
# If we still have more than 2 types, shrink window normally
23+
while len(basket) > 2:
24+
basket[fruits[left]] -= 1
25+
if basket[fruits[left]] == 0:
26+
del basket[fruits[left]]
27+
left += 1
28+
29+
max_fruits = max(max_fruits, right - left + 1)
30+
31+
return max_fruits

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ This repository is created for learning and educational purposes. The goal is to
1515

1616
| Date | Problem ID | Problem Title | Difficulty | Folder Link |
1717
|------|------------|---------------|------------|-------------|
18+
| 2025-08-05 | 2107 | Fruits Into Baskets II | Hard | [Link](Hard/2025-08-05-2107-FruitsIntoBasketsII/) |
1819
| 2025-08-04 | 904 | Fruit Into Baskets | Medium | [Link](Medium/2025-08-04-0904-FruitIntoBaskets/) |
1920
| 2025-08-03 | 2106 | Maximum Fruits Harvested After at Most K Steps | Hard | [Link](Hard/2025-08-03-2106-MaximumFruitsHarvestedAfterAtMostKSteps/) |
2021
| 2025-08-02 | 2857 | Rearranging Fruits | Hard | [Link](Hard/2025-08-02-2857-RearrangingFruits/) |
@@ -50,10 +51,10 @@ YYYY-MM-DD-ProblemID-Title/
5051

5152
## Statistics
5253

53-
- **Total Problems Solved**: 8
54+
- **Total Problems Solved**: 9
5455
- **Easy**: 1
5556
- **Medium**: 5
56-
- **Hard**: 2
57+
- **Hard**: 3
5758

5859
## Getting Started
5960

0 commit comments

Comments
 (0)