What the problem really asks
Re-read the problem: "longest contiguous subarray containing at most two distinct values". The basket rules are just flavour text — once you start, you walk right collecting one fruit per tree and you can hold up to two distinct types. So the answer is the maximum window length such that the window contains at most 2 distinct fruit types.
The sliding window
Imagine a window [left, right] over the array, plus a small dictionary count that says how many trees of each fruit type are currently inside the window:
fruits = [3, 3, 3, 1, 2, 1, 1, 2, 3, 3, 4]
[left ............. right]
{1: 3, 2: 2} ← only 2 distinct, OK
We grow right one step at a time. Whenever the window collects a third distinct fruit, we shrink from the left until only two types remain.
Steps
- Initialise
count = {}, left = 0, best = 0.
- For each
right from 0 to n - 1:
- Add
fruits[right] to the window: count[fruits[right]] += 1.
- Shrink while invalid: while the window has more than 2 distinct types,
- Decrement
count[fruits[left]]. If that count reaches 0, remove the key — that fruit type no longer exists in the window.
left += 1.
- Update
best = max(best, right - left + 1).
- Return
best.
Walk-through
fruits = [3, 3, 3, 1, 2, 1, 1, 2, 3, 3, 4]. We follow the window step by step.
| right | fruit added | window | count | shrink? | best |
|---|
| 0 | 3 | [3] | {3:1} | no | 1 |
| 1 | 3 | [3,3] | {3:2} | no | 2 |
| 2 | 3 | [3,3,3] | {3:3} | no | 3 |
| 3 | 1 | [3,3,3,1] | {3:3, 1:1} | no | 4 |
| 4 | 2 | [3,3,3,1,2] | {3:3,1:1,2:1} | yes — drop 3s until only 2 types left, left → 3 | 4 |
| 5 | 1 | [1,2,1] | {1:2, 2:1} | no | 4 |
| 6 | 1 | [1,2,1,1] | {1:3, 2:1} | no | 4 |
| 7 | 2 | [1,2,1,1,2] | {1:3, 2:2} | no | 5 |
| 8 | 3 | [1,2,1,1,2,3] | {1:3,2:2,3:1} | yes — left advances past 1s and 2s until left=8 | 5 |
| 9 | 3 | [3,3] | {3:2} | no | 5 |
| 10 | 4 | [3,3,4] | {3:2, 4:1} | no | 5 |
Answer: 5.
Why each pointer is monotone
right only moves forward (it's the loop variable). left only moves forward (the while loop only ever advances it). So each index is added at most once and removed at most once — that's the linear-time guarantee.
Edge cases
- Single fruit type throughout: the inner
while never fires; the answer is n.
n == 1: loop runs once, best = 1.
Complexity
- Time:
O(n) — each index touched at most twice.
- Space:
O(1) — the counter holds at most 3 entries (it spikes to 3, then we shrink it back to 2).