871 - Minimum Number of Refueling Stops
Difficulty: Hard | Pattern: Greedy + Max-Heap | Company tags: Amazon, Google, Uber
Problem Statement
A car travels from a starting position to a destination which is target miles away.
Along the way, there are gas stations at positions stations[i] = [position, fuel]. Note that fuel is in liters.
The car starts with an infinite tank but a finite initial fuel. The car uses 1 liter per mile.
Return the minimum number of refueling stops the car must make to reach the destination. If it cannot reach the destination, return -1.
Example 1:
Input: target=100, startFuel=10, stations=[[10,60],[20,30],[30,30],[60,40]]
Output: 2
Approach: Greedy + Max-Heap — O(n log n)
Key insight: Drive as far as possible. When you can't continue, "retrospectively" pick the largest fuel you passed. This is optimal because we're maximizing fuel gained per stop.
import heapq
def minRefuelStops(target: int, startFuel: int, stations: list[list[int]]) -> int:
fuel = startFuel
stops = 0
prev = 0
heap = [] # max-heap (negate values)
for position, gas in stations + [[target, 0]]:
fuel -= position - prev # drive to this station
while heap and fuel < 0:
fuel += -heapq.heappop(heap) # use largest fuel seen so far
stops += 1
if fuel < 0:
return -1
heapq.heappush(heap, -gas)
prev = position
return stops
Algorithm Flow
Dry Run
target=100, startFuel=10, stations=[[10,60],[20,30],[30,30],[60,40]]
We drive station-to-station. Whenever fuel drops below 0, we pop the largest fuel amount seen so far (one stop) and repeat until fuel >= 0.
| position | drive cost | fuel after driving | action | fuel (end) | stops | heap after |
|---|---|---|---|---|---|---|
| 10 | 10 - 0 = 10 | 0 | fuel ≥ 0, no pop; push 60 | 0 | 0 | [60] |
| 20 | 20 - 10 = 10 | -10 | pop 60 → -10 + 60 = 50 (stop); push 30 | 50 | 1 | [30] |
| 30 | 30 - 20 = 10 | 40 | fuel ≥ 0, no pop; push 30 | 40 | 1 | [30, 30] |
| 60 | 60 - 30 = 30 | 10 | fuel ≥ 0, no pop; push 40 | 10 | 1 | [40, 30, 30] |
| 100 (target) | 100 - 60 = 40 | -30 | pop 40 → -30 + 40 = 10 (stop); 10 ≥ 0, stop popping | 10 | 2 | [30, 30] |
At the virtual target station, one pop of the largest passed fuel (40) is enough: -30 + 40 = 10, which is non-negative, so the while loop halts immediately. Final answer: 2 stops.
Note that the heap deliberately holds fuel values we passed but never needed (the two 30s here). That is the point of the retrospective approach — those stations stay available as options but cost us nothing because we never popped them.
Complexity
- Time: O(n log n)
- Space: O(n)
Key Terms
| Term | Definition |
|---|---|
| Greedy | Making the locally optimal choice at each step, trusting it leads to a globally optimal solution. |
| Max-heap | A priority queue that always exposes the largest element; here it tracks the biggest fuel amount passed but not yet used. |
| Retrospective selection | Deferring a decision (which station to "use") until it's forced, then picking the best option seen so far. |
| Amortized stop count | The number of refuels is bounded by the number of stations, since each station is pushed/popped at most once. |
FAQ
- Why use a max-heap instead of trying every subset of stations? Trying subsets is exponential. The greedy insight — always keep the option to "retroactively" use the largest fuel passed — lets a single pass with a heap reach the optimal answer in O(n log n).
- Can this be solved without extra space? Not efficiently while keeping O(n log n) time — the heap is what lets you defer the choice of which station to use. A DP approach (max distance reachable with k stops) avoids a heap but uses O(n) space too, trading time for a different structure.
- What if
startFuel >= targetalready? The loop still runs correctly: fuel never goes negative, sostopsstays 0 and the function returns immediately with 0 stops. - What if the car can never reach the target? Once fuel goes negative and the heap is empty (no more stations to draw fuel from), the function returns -1.
- What's the common follow-up interviewers ask?
"What's the maximum distance reachable with at most k stops?" — this flips the problem into a DP formulation:
dp[k]= farthest distance reachable using exactly k stops, updated greedily as stations are processed in order.
Quick Revision
- Pattern: Greedy + Max-Heap ("retroactive" greedy).
- Process stations in position order; subtract distance traveled from current fuel.
- If fuel goes negative, pop the largest fuel value from the heap (best station passed so far) and add it — that's one stop.
- If fuel still negative after exhausting the heap, destination is unreachable → return -1.
- Push each station's fuel onto the heap after "arriving" at it, whether or not it's used yet.
- Append a virtual station
[target, 0]to trigger the final check. - Time: O(n log n) from heap operations; Space: O(n) for the heap.
- Each station is pushed and popped at most once, bounding total heap work.
- Alternative: DP where
dp[k]tracks max distance reachable with k refuels — O(n²) time, O(n) space.
Related Problems
- 1642 - Furthest Building You Can Reach — same "retroactive greedy with a heap" pattern.
- 1383 - Maximum Performance of a Team — greedy + min-heap to track a bounded set of best choices.
- Jump Game II (LeetCode 45) — related greedy problem about minimizing the number of jumps to reach the end.