904. Fruit Into Baskets
Quick Identifier
- Topic: sliding-window
- Pattern: Sliding Window (Variable Window with At Most K Distinct)
- Hint: Translate the convoluted "fruit basket" metaphor into standard DSA terms: "Find the longest contiguous subarray containing at most 2 distinct integers."
- Difficulty: Medium
- Companies: Google, Amazon, Meta, Apple, DoorDash
- LeetCode: 904
Quick Sights
- Time Complexity:
O(n). We visit each tree exactly once with therightpointer, and at most once with theleftpointer. Hash map insertions and deletions are $O(1)$. - Space Complexity:
O(1). The hash map stores the frequencies of the fruit types currently in the window. Because we strictly limit the window to contain at most 3 distinct types before shrinking it back to 2, the map's size never exceeds 3. - Core Mechanism: Maintain a sliding window and a frequency map of fruit types. Expand
right. If adding a new fruit pushes the size of your frequency map to 3 (meaning 3 distinct fruit types are in the window), your window is invalid. Shrinkleftuntil the frequency of one fruit type drops to 0. Delete that fruit from the map to restore validity (map size back to 2).
Concept Explanation
As a senior engineer, the first step is to strip away the "story" of the problem. You are given an array of integers and asked to find the maximum length of a subarray that contains no more than two unique values. This is literally just a hard-coded $K=2$ instance of the generic "Longest Substring with At Most K Distinct Characters" template.
- The Expansion (Picking Fruit): Start walking down the row of trees (
rightpointer). Throw every fruit you encounter into your baskets (frequency map). Keep track of how many of each type you have. - The Limit (3rd Type): The moment you encounter a fruit that forces your frequency map to have 3 keys, you've violated the physical constraint (you only have 2 baskets). The current sequence of trees is broken.
- The Contraction (Emptying a Basket): To fix the violation, you must walk your
leftpointer forward, abandoning trees. For every tree you pass, remove one fruit of that type from your map. - The Reset: You continue abandoning trees from the left until one of the fruit types completely runs out (its count hits 0). The moment a count hits 0, you physically throw that basket away (
deletefrom the map). Now you only have 2 types of fruit again, the window is valid, and you can resume picking new fruits.
Diagram
This flowchart outlines the window expansion and map maintenance.
Loading diagram…
Study Pattern (SDE3+)
- 0–3 min: Immediately translate the problem text. State clearly: "This asks for the longest subarray with at most 2 distinct elements." Note the $O(1)$ space complexity because the map is bounded to size 3.
- Implementation pass: Be extremely careful to completely
deletethe key from the map when its value reaches 0. If you leave{fruitType: 0}in the map,map.sizeorlen(map)will still report 3, and yourwhileloop will run infinitely or shrink the window to nothing. - 5 min extension: Discuss how to generalize this to $K$ baskets. The code is identical, just replace
> 2with> K. If the interviewer asks to solve the "Subarrays with Exactly K Distinct" variant, explain the math trick:AtMost(K) - AtMost(K-1).
Approaches
- Brute force — Check every possible subarray, put elements in a Set, if
Set.size <= 2, check length. Time: $O(n^3)$. - Sliding Window (Optimized) — Maintain a frequency map. Expand right, shrink left when the map exceeds 2 keys. Time: $O(n)$. (Always pick this)
Optimal Solution
Go
func totalFruit(tree []int) int {
counts := make(map[int]int)
left := 0
best := 0
for right := 0; right < len(tree); right++ {
fruit := tree[right]
counts[fruit]++
// If we have more than 2 types of fruit, the window is invalid.
// We must shrink from the left until a fruit type is completely removed.
for len(counts) > 2 {
leftFruit := tree[left]
counts[leftFruit]--
// Critical step: completely remove the key so len(counts) drops
if counts[leftFruit] == 0 {
delete(counts, leftFruit)
}
left++
}
width := right - left + 1
if width > best {
best = width
}
}
return best
}JavaScript
function totalFruit(tree) {
const counts = new Map();
let left = 0;
let best = 0;
for (let right = 0; right < tree.length; right++) {
const fruit = tree[right];
counts.set(fruit, (counts.get(fruit) || 0) + 1);
// Shrink window if we have more than 2 distinct fruit types
while (counts.size > 2) {
const leftFruit = tree[left];
counts.set(leftFruit, counts.get(leftFruit) - 1);
// Critical step: completely remove the key so counts.size drops
if (counts.get(leftFruit) === 0) {
counts.delete(leftFruit);
}
left++;
}
const width = right - left + 1;
if (width > best) {
best = width;
}
}
return best;
}Walkthrough
Input: tree = [3,3,3,1,2,1,1,2,3,3,4]
| right | tree[right] | Window | counts map | valid? (size $\le$ 2) | left | best |
|---|---|---|---|---|---|---|
| 0,1,2 | 3 | [3,3,3] | {3:3} | Yes | 0 | 3 |
| 3 | 1 | [3,3,3,1] | {3:3, 1:1} | Yes | 0 | 4 |
| 4 | 2 | [3,3,3,1,2] | {3:3, 1:1, 2:1} | No (size 3). Shrink left until 3 drops to 0. | 3 | 4 |
| (still 4) | 2 | [1,2] | {1:1, 2:1} | Yes | 3 | 4 |
| 5,6,7 | 1,1,2 | [1,2,1,1,2] | {1:3, 2:2} | Yes | 3 | 5 |
| 8 | 3 | [1,2,1,1,2,3] | {1:3, 2:2, 3:1} | No (size 3). Shrink left until 1 drops to 0. | 8 | 5 |
| (still 8) | 3 | [3] | {3:1} | Yes | 8 | 5 |
| 9 | 3 | [3,3] | {3:2} | Yes | 8 | 5 |
| 10 | 4 | [3,3,4] | {3:2, 4:1} | Yes | 8 | 5 |
Output: 5
Edge Cases
treehas length 1 or 2 (Returnstree.lengthimmediately, as it's impossible to exceed 2 types).- All trees are the exact same fruit type (Returns
tree.length).
Pitfalls
- Storing the actual indices of the fruits instead of their counts. The window validity depends entirely on the frequency of the types, not their positions.
- Forgetting to
deletethe key from the map when the count reaches0. If you just domap[fruit] = 0, the map still physically contains the key, andmap.sizewill remain too high.
Similar Problems
- 424. Longest Repeating Character Replacement — budgeted uniformity vs distinct cap.
- 3. Longest Substring Without Repeating — all distinct (which is functionally At Most
right - left + 1Distinct). - 992. Subarrays with K Different Integers — The Hard variant.
Mind-Map Tags
#sliding-window #at-most-k-distinct #two-types #frequency-map #longest-window
Mark this page when you finish learning it.
Last updated on
Spotted something unclear or wrong on this page?