Minimum Number of Refueling Stops is a classic greedy + heap problem. It teaches a beautiful trick: make decisions as late as possible. Instead of deciding to refuel the moment you pass a station, you remember every station you pass and only "spend" one when you are actually stuck — and then you spend the best one available.
Problem. A car starts with start fuel and wants to reach position target. Each station is
[position, fuel] and is passed in increasing position order. Driving one unit costs one unit of fuel.
At a station you may add its fuel to your tank. Return the minimum number of stops to reach the
target, or -1 if it is impossible.
Example: target = 100, start = 10, stations = [[10,60],[20,30],[30,30],[60,40]] → answer 2
(refuel at the 60-fuel station, then at the 40-fuel station).
The slow way first
You could try every subset of stations and check which combinations reach the target with the fewest stops — but that is exponential. A smarter brute force is dynamic programming over dp[k] = the farthest you can reach using k stops, which is O(n²). It works, but for large inputs it is slow, and it hides the simple intuition.
The question to ask: when I am forced to refuel, which station should I have stopped at? The answer is obvious in hindsight — the one with the most fuel among the stations I have already driven past.
The idea: bank every station, spend the biggest when stuck
Drive forward greedily. Every station you can reach, you do not refuel at — you just drop its fuel into a max-heap as a future option. When your range can no longer reach the next station (or the target), you are stuck: pop the largest fuel from the heap, count it as one stop, and add it to your range. Repeat until you reach the target or the heap is empty (impossible).
The key insight: deferring the choice lets us always make the optimal refuel decision, because by the time we are stuck we have seen every station that was ever reachable for free.
Walk through it
Step through the animation. The range pointer marks how far we can drive. As we pass each station, its fuel joins the heap label. At position 20 we stall — pop the biggest (60), one stop, range jumps to 70. We coast past the rest, then stall again before the target — pop 40, second stop, range hits 110. Done in 2 stops.
Pseudocode
heap = empty max-heap, fuel = start, stops = 0, i = 0
while fuel < target:
while next station's position <= fuel: # every reachable station
push its fuel onto the max-heap
advance i
if heap is empty:
return -1 # stuck, cannot reach target
fuel += pop the largest fuel from the heap # spend the best option
stops += 1
return stopsThe Python solution
def min_refuel(target, start, stations):
heap, fuel, stops, i = [], start, 0, 0
while fuel < target:
while i < len(stations) and stations[i][0] <= fuel:
heappush(heap, -stations[i][1])
i += 1
if not heap:
return -1
fuel += -heappop(heap)
stops += 1
return stopsheapis a max-heap of fuel from stations we have already passed. Python only has a min-heap, so we push negatives (-stations[i][1]) and negate again on pop.- The outer
while fuel < targetloops until our range covers the target. - The inner
whilebanks every station whose position is within our current range, pushing its fuel and advancingi. if not heap: return -1means we are stuck with no banked station — the target is unreachable.- Lines 9–10 are the greedy move: pop the largest fuel, extend our range, and count one stop.
Complexity
| Case | Time | Notes |
|---|---|---|
| DP over stops | O(n²) (slow) | dp[k] = farthest with k stops |
| Greedy + heap (this solution) | O(n log n) (moderate) | each station pushed/popped once |
O(n) (moderate)Each station is pushed and popped from the heap at most once, and heap operations are O(log n), giving O(n log n). The heap holds at most every station, so the extra space is O(n).
When this pattern shows up
When a problem lets you "use" options you encounter as you move forward, and you want the fewest (or best) uses, think: scan forward, bank options in a heap, and greedily spend the biggest only when forced. This "decide as late as possible" idea also powers problems like IPO, task scheduling, and course scheduling with deadlines.
Python's heapq is a min-heap. To simulate a max-heap, push the negation of each value and negate
again when you pop. Forgetting the sign is the most common bug here.
Practice
After driving to range 70 and passing the stations at 20, 30, and 60, the heap holds {40, 30, 30}. The target is 100. Which fuel do we pop, and what is the new range?
1. Why do we wait until we are stuck before refueling, instead of refueling at the first station?
2. What does the max-heap store?
3. How do you simulate a max-heap with Python's heapq?
4. What is the overall time complexity of the greedy + heap solution?