Facebook Pixel

871. Minimum Number of Refueling Stops

Problem Description

You have a car that needs to travel from a starting position to a destination that is target miles east of the starting position.

Along the route, there are gas stations represented by an array stations, where each stations[i] = [position_i, fuel_i] means:

  • The i-th gas station is located position_i miles east of the starting position
  • This station has fuel_i liters of gas available

The car operates under these rules:

  • It starts with startFuel liters of fuel in its tank (which has infinite capacity)
  • It consumes exactly 1 liter of gas per mile driven
  • When reaching a gas station, the car can choose to stop and refuel, taking all the gas from that station
  • The car can refuel at a station even if it arrives with 0 fuel remaining
  • Reaching the destination with 0 fuel counts as successfully arriving

Your task is to find the minimum number of refueling stops needed to reach the destination. If it's impossible to reach the destination, return -1.

For example, if target = 100, startFuel = 10, and stations = [[10, 60], [20, 30], [30, 30], [60, 40]]:

  • The car can drive 10 miles to the first station
  • Refuel with 60 liters (1 stop)
  • Drive to mile 60 and refuel with 40 liters (2 stops)
  • Now with enough fuel to reach mile 100
  • Total minimum stops: 2
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that we want to minimize the number of stops, so we should be strategic about which gas stations to use. We don't need to decide immediately whether to refuel at each station we pass - instead, we can keep track of all the stations we've passed and make refueling decisions retroactively when we run out of fuel.

Think of it this way: as we drive along, we "collect" information about available gas stations. When we run out of fuel, we ask ourselves: "Of all the stations I've already passed, which one would have been the best to stop at?" The answer is always the station with the most fuel, as this maximizes how far we can continue driving.

This suggests a greedy strategy:

  1. Drive as far as possible with current fuel
  2. When we can't reach the next station (or destination), look back at all passed stations
  3. Retroactively "refuel" at the station with the most fuel
  4. Repeat until we can reach the next checkpoint

Why does this work? Because if we must make k stops, choosing the k stations with the largest fuel amounts will always give us the maximum possible range. Any other selection would give us less total fuel and potentially require more stops.

The beauty of this approach is that we don't need to make decisions in advance. We can simulate driving forward, and whenever we get stuck, we look back and pick the best option we skipped. This is implemented using a max-heap to efficiently track and retrieve the largest fuel amount from stations we've passed.

By adding [target, 0] to the stations list, we treat the destination as a final "station" with no fuel, simplifying our loop logic to handle reaching the destination uniformly with reaching any other station.

Learn more about Greedy, Dynamic Programming and Heap (Priority Queue) patterns.

Solution Approach

The solution implements the greedy strategy using a max-heap (priority queue) to efficiently track and retrieve the gas stations with the most fuel.

Data Structure: We use a max-heap pq to store fuel amounts from passed stations. In Python, heapq provides a min-heap, so we store negative values to simulate a max-heap.

Algorithm Steps:

  1. Initialize variables:

    • pq = [] - empty heap to store fuel from passed stations
    • ans = 0 - count of refueling stops
    • pre = 0 - previous position (starts at 0)
    • Append [target, 0] to stations to treat destination as final checkpoint
  2. Process each station/checkpoint:

    for pos, fuel in stations:
        dist = pos - pre          # distance to travel
        startFuel -= dist         # consume fuel for this distance
  3. Handle fuel shortage (greedy refueling):

    while startFuel < 0 and pq:
        startFuel -= heappop(pq)  # take largest fuel (negative value)
        ans += 1                   # increment stop count
    • If startFuel < 0, we don't have enough fuel to reach current position
    • Pop the largest fuel amount from heap (most negative value)
    • Add it to our fuel tank (subtracting negative = adding positive)
    • Count this as a refueling stop
  4. Check feasibility:

    if startFuel < 0:
        return -1
    • If still negative after using all available stations, it's impossible
  5. Record current station:

    heappush(pq, -fuel)   # store as negative for max-heap behavior
    pre = pos             # update position
    • Add current station's fuel to heap (we might use it later)
    • Update our position marker

Why this works:

  • We only refuel when absolutely necessary (fuel goes negative)
  • When we must refuel, we choose the best option from our past (largest fuel)
  • The heap ensures we can always get the maximum fuel in O(log n) time
  • Total time complexity: O(n log n) where n is the number of stations

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's trace through a small example to illustrate the solution approach.

Example: target = 25, startFuel = 10, stations = [[5, 5], [15, 10], [20, 5]]

Step-by-step execution:

Initial Setup:

  • Add destination as a checkpoint: stations = [[5, 5], [15, 10], [20, 5], [25, 0]]
  • pq = [] (max-heap for fuel amounts)
  • ans = 0 (refuel stops)
  • pre = 0 (current position)
  • startFuel = 10

Processing Station [5, 5]:

  • Distance to travel: 5 - 0 = 5
  • Fuel after travel: 10 - 5 = 5
  • Fuel is positive, so we can reach this station
  • Add station's fuel to heap: pq = [-5] (negative for max-heap)
  • Update position: pre = 5

Processing Station [15, 10]:

  • Distance to travel: 15 - 5 = 10
  • Fuel after travel: 5 - 10 = -5
  • Fuel is negative! We need to refuel retroactively
  • Pop largest fuel from heap: -(-5) = 5, so startFuel = -5 + 5 = 0
  • Increment stops: ans = 1
  • Now fuel is 0 (just enough to reach)
  • Add station's fuel to heap: pq = [-10]
  • Update position: pre = 15

Processing Station [20, 5]:

  • Distance to travel: 20 - 15 = 5
  • Fuel after travel: 0 - 5 = -5
  • Fuel is negative! Need to refuel again
  • Pop largest fuel from heap: -(-10) = 10, so startFuel = -5 + 10 = 5
  • Increment stops: ans = 2
  • Now fuel is 5 (enough to reach)
  • Add station's fuel to heap: pq = [-5]
  • Update position: pre = 20

Processing Destination [25, 0]:

  • Distance to travel: 25 - 20 = 5
  • Fuel after travel: 5 - 5 = 0
  • Fuel is exactly 0 (we reach destination)
  • No need to refuel

Result: Minimum stops = 2

The key insight: We drove past the first station without stopping initially, but when we couldn't reach the second station, we retroactively "decided" to refuel there. Similarly, we took fuel from the second station when we couldn't reach the third. This greedy approach of always choosing the station with the most fuel when needed guarantees the minimum number of stops.

Solution Implementation

1class Solution:
2    def minRefuelStops(
3        self, target: int, startFuel: int, stations: List[List[int]]
4    ) -> int:
5        # Max heap to store available fuel amounts (negative values for max heap)
6        max_heap = []
7      
8        # Initialize counters
9        refuel_count = 0
10        previous_position = 0
11      
12        # Add the target as a final station with 0 fuel to simplify logic
13        stations.append([target, 0])
14      
15        # Process each station (including the target)
16        for position, fuel_available in stations:
17            # Calculate distance from previous position
18            distance = position - previous_position
19          
20            # Consume fuel for the distance traveled
21            startFuel -= distance
22          
23            # If we don't have enough fuel, refuel from the largest available stations
24            while startFuel < 0 and max_heap:
25                # Pop the maximum fuel amount (negative value, so negate it back)
26                startFuel -= heappop(max_heap)
27                refuel_count += 1
28          
29            # If still not enough fuel after using all available stations, impossible
30            if startFuel < 0:
31                return -1
32          
33            # Add current station's fuel to available options (negative for max heap)
34            heappush(max_heap, -fuel_available)
35          
36            # Update previous position for next iteration
37            previous_position = position
38      
39        return refuel_count
40
1class Solution {
2    public int minRefuelStops(int target, int startFuel, int[][] stations) {
3        // Max heap to store fuel amounts from stations we've passed
4        // We prioritize using stations with more fuel when we need to refuel
5        PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);
6      
7        int numStations = stations.length;
8        int refuelCount = 0;
9        int previousPosition = 0;
10      
11        // Process each station and finally the target position
12        for (int i = 0; i <= numStations; i++) {
13            // Current position is either the next station or the target
14            int currentPosition = (i < numStations) ? stations[i][0] : target;
15          
16            // Calculate distance from previous position to current position
17            int distance = currentPosition - previousPosition;
18          
19            // Consume fuel for the distance traveled
20            startFuel -= distance;
21          
22            // If we don't have enough fuel, refuel from the best stations we've passed
23            while (startFuel < 0 && !maxHeap.isEmpty()) {
24                // Refuel from the station with the most fuel available
25                startFuel += maxHeap.poll();
26                refuelCount++;
27            }
28          
29            // If still not enough fuel after using all available stations, impossible to reach
30            if (startFuel < 0) {
31                return -1;
32            }
33          
34            // If we're at a station (not the target), add its fuel to available options
35            if (i < numStations) {
36                maxHeap.offer(stations[i][1]);
37                previousPosition = stations[i][0];
38            }
39        }
40      
41        return refuelCount;
42    }
43}
44
1class Solution {
2public:
3    int minRefuelStops(int target, int startFuel, vector<vector<int>>& stations) {
4        // Max heap to store available fuel amounts from passed stations
5        priority_queue<int> maxHeap;
6      
7        // Add target position as a dummy station with 0 fuel to unify logic
8        stations.push_back({target, 0});
9      
10        int refuelCount = 0;      // Number of refueling stops made
11        int previousPosition = 0; // Track previous position
12      
13        // Process each station (including the target)
14        for (const auto& station : stations) {
15            int currentPosition = station[0];
16            int availableFuel = station[1];
17          
18            // Calculate distance from previous position to current station
19            int distance = currentPosition - previousPosition;
20          
21            // Consume fuel for the distance traveled
22            startFuel -= distance;
23          
24            // If fuel becomes negative, refuel from previously passed stations
25            // Choose stations with maximum fuel (greedy approach)
26            while (startFuel < 0 && !maxHeap.empty()) {
27                startFuel += maxHeap.top();  // Refuel with max available fuel
28                maxHeap.pop();
29                ++refuelCount;                // Increment refuel counter
30            }
31          
32            // If still insufficient fuel after using all available stations
33            if (startFuel < 0) {
34                return -1;
35            }
36          
37            // Add current station's fuel to available options for future use
38            maxHeap.push(availableFuel);
39          
40            // Update previous position for next iteration
41            previousPosition = currentPosition;
42        }
43      
44        return refuelCount;
45    }
46};
47
1/**
2 * Calculates the minimum number of refueling stops needed to reach the target
3 * @param target - The target distance to reach
4 * @param startFuel - The initial amount of fuel in the tank
5 * @param stations - Array of fuel stations, where each station is [position, fuel_amount]
6 * @returns The minimum number of refueling stops, or -1 if impossible
7 */
8function minRefuelStops(target: number, startFuel: number, stations: number[][]): number {
9    // Max heap to store fuel amounts from passed stations (greedy approach)
10    const maxHeap = new MaxPriorityQueue<number>();
11  
12    // Initialize refuel count and previous position
13    let refuelCount = 0;
14    let previousPosition = 0;
15  
16    // Add target as a final station with 0 fuel to simplify logic
17    stations.push([target, 0]);
18  
19    // Process each station
20    for (const [position, fuelAmount] of stations) {
21        // Calculate distance from previous position to current station
22        const distance = position - previousPosition;
23      
24        // Consume fuel for the distance traveled
25        startFuel -= distance;
26      
27        // If we don't have enough fuel, refuel from the best available stations
28        while (startFuel < 0 && !maxHeap.isEmpty()) {
29            // Refuel with the maximum available fuel from passed stations
30            startFuel += maxHeap.dequeue();
31            refuelCount++;
32        }
33      
34        // If still not enough fuel after using all available stations, impossible to reach
35        if (startFuel < 0) {
36            return -1;
37        }
38      
39        // Add current station's fuel to available options for future use
40        maxHeap.enqueue(fuelAmount);
41      
42        // Update previous position for next iteration
43        previousPosition = position;
44    }
45  
46    return refuelCount;
47}
48

Time and Space Complexity

Time Complexity: O(n × log n)

The algorithm iterates through all n gas stations once. For each station:

  • Computing the distance and updating fuel takes O(1) time
  • In the worst case, we might need to pop from the heap multiple times. Across all iterations, each station's fuel can be pushed to the heap once and popped at most once
  • Each heap push operation (heappush) takes O(log n) time
  • Each heap pop operation (heappop) takes O(log n) time
  • Since we have at most n push operations and at most n pop operations total across all iterations, the overall time complexity is O(n × log n)

Space Complexity: O(n)

The space is primarily used by:

  • The priority queue pq which can store at most n fuel values (one from each station) at any point, requiring O(n) space
  • The modified stations list with the appended target station adds one extra element, but this doesn't change the asymptotic space complexity
  • Other variables (ans, pre, startFuel, etc.) use O(1) space

Therefore, the total space complexity is O(n).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Forgetting to Handle the Target Position

Pitfall: Many implementations forget that we need to check if we can reach the target itself, not just the last gas station. If the last station is at position 70 but the target is at 100, we still need 30 more units of fuel.

Solution: The code handles this elegantly by appending [target, 0] to the stations list. This treats the destination as a final checkpoint with no fuel, ensuring we verify we can reach it.

2. Using Min-Heap Instead of Max-Heap

Pitfall: Python's heapq is a min-heap by default. If you directly push positive fuel values and pop them thinking you'll get the maximum, you'll actually get the minimum fuel amount, leading to suboptimal refueling choices.

# Wrong approach - gets minimum fuel
heappush(max_heap, fuel_available)  
fuel_to_add = heappop(max_heap)  # This gives MINIMUM fuel!

Solution: Store negative values to simulate max-heap behavior:

# Correct approach - simulates max-heap
heappush(max_heap, -fuel_available)  # Store as negative
fuel_to_add = -heappop(max_heap)     # Negate back to get maximum

3. Refueling Before It's Necessary

Pitfall: Some might think to refuel at every station with large fuel amounts preemptively. This doesn't minimize the number of stops.

# Wrong greedy approach
for position, fuel_available in stations:
    if fuel_available > threshold:  # Some arbitrary threshold
        startFuel += fuel_available
        refuel_count += 1

Solution: Only refuel when fuel becomes negative (when we absolutely must). The current implementation correctly uses:

while startFuel < 0 and max_heap:  # Only refuel when necessary
    startFuel -= heappop(max_heap)
    refuel_count += 1

4. Not Tracking Previous Position Correctly

Pitfall: Calculating distance from the starting position (0) each time instead of from the previous position leads to incorrect fuel consumption.

# Wrong - always calculating from start
for position, fuel_available in stations:
    startFuel -= position  # This assumes we're always starting from 0!

Solution: Track and update the previous position:

distance = position - previous_position  # Correct incremental distance
startFuel -= distance
previous_position = position  # Update for next iteration

5. Adding Station Fuel Before Checking Reachability

Pitfall: Adding the current station's fuel to the heap before verifying we can actually reach that station.

# Wrong order
for position, fuel_available in stations:
    heappush(max_heap, -fuel_available)  # Added before checking!
    distance = position - previous_position
    startFuel -= distance
    # What if startFuel < 0 and max_heap is empty?

Solution: First verify we can reach the station (refuel if needed), then add its fuel to available options:

# Correct order
startFuel -= distance
while startFuel < 0 and max_heap:  # Check and refuel if needed
    startFuel -= heappop(max_heap)
    refuel_count += 1
if startFuel < 0:
    return -1  # Can't reach this station
heappush(max_heap, -fuel_available)  # Now safe to add
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Consider the classic dynamic programming of longest increasing subsequence:

Find the length of the longest subsequence of a given sequence such that all elements of the subsequence are sorted in increasing order.

For example, the length of LIS for [50, 3, 10, 7, 40, 80] is 4 and LIS is [3, 7, 40, 80].

What is the recurrence relation?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More