Coding Interview Patterns Module 27 – Greedy algorithms and proofs
Gas station problem: where the circular tour starts
Find the pump from which a circular tour never runs dry with one running-balance scan, prove why restarting is safe, and return -1 when fuel falls short.
What you will learn
- Find the starting point of a circular tour with one running-balance scan
- Prove why restarting after a negative balance is safe
- Decide that no start exists exactly when the total balance is negative
- Read the answer off the lowest point of the running balance
Before you start
What you’ll learn · Before you start
What you will learn
- Find the starting point of a circular tour with one running-balance scan
- Prove why restarting after a negative balance is safe
- Decide that no start exists exactly when the total balance is negative
- Read the answer off the lowest point of the running balance
Before you start
On this page
The gas station problem asks where to start a circular tour. Pumps stand around a loop, each gives some fuel, each leg to the next pump costs some, and the tank starts empty. A start exists exactly when the pumps give at least as much fuel in total as the legs cost. To find it, drive once from pump 0 with a running tank, and whenever the tank goes below zero, start again at the next pump: no pump since the last start could have worked. That is one pass, O(n), and the last start it chooses is the smallest pump that completes the loop.
The problem: a van on a ring road
The problem
A delivery van drives once round a ring road with n fuel pumps, numbered 0 to n − 1 in the direction of travel.
At pump i it takes on gain[i] litres, and the leg from pump i to the next pump, from the last back to pump 0,
uses cost[i] litres. The tank starts empty and holds any amount. Return the smallest pump from which the van can
drive the whole loop without the tank going below zero, or −1 if there is none. Limits: 1 ≤ n ≤ 10⁶ and
0 ≤ gain[i], cost[i] ≤ 10⁴.
gain = [3, 1, 7, 2, 5, 0],cost = [4, 3, 2, 4, 1, 3]gives 2, the only pump that works, as in the figure.gain = [1, 2, 3],cost = [2, 3, 2]gives −1: the pumps give 6 litres and the legs cost 7.gain = [2, 2, 2],cost = [2, 2, 2]gives 0: every pump works, and 0 is the smallest.
The six-pump ring of the first example
Text description of the diagram
The figure shows six pumps, numbered 0 to 5, on a circle, driven clockwise. Beside each pump is the fuel it gives, and inside the circle, on each leg, the fuel that leg costs.
- Pump 0 gives 3 litres; the leg to pump 1 costs 4.
- Pump 1 gives 1 litre; the leg to pump 2 costs 3.
- Pump 2 gives 7 litres; the leg to pump 3 costs 2.
- Pump 3 gives 2 litres; the leg to pump 4 costs 4.
- Pump 4 gives 5 litres; the leg to pump 5 costs 1.
- Pump 5 gives nothing; the leg back to pump 0 costs 3.
The pumps give 18 litres in all and the legs cost 17. Pump 2 is shaded: it is the only pump from which the van can drive the whole loop without its tank going below zero.
The brute force, and why it fails at these sizes
Try each pump as the start, smallest first, and drive until the tank goes below zero or the loop closes:
# The ring road of the lesson: pump i gives gain[i] litres, and the leg from pump i to the next costs cost[i] litres.
GAIN = [3, 1, 7, 2, 5, 0]
COST = [4, 3, 2, 4, 1, 3]
def start_by_trying_each(gain, cost):
"""Tries pump 0, then 1, ...: drives the loop until the tank would go below zero. Returns (start, legs driven)."""
n, legs = len(gain), 0
for start in range(n):
tank = 0
for k in range(n):
i = (start + k) % n
tank += gain[i] - cost[i]
legs += 1
if tank < 0: # the van runs dry on the leg out of pump i
break
else:
return start, legs
return -1, legs
print("the six-pump ring:", start_by_trying_each(GAIN, COST))
print("gain [1, 2, 3], cost [2, 3, 2]:", start_by_trying_each([1, 2, 3], [2, 3, 2]))
for n in (1_000, 2_000, 4_000):
cost = [1] * (n - 1) + [2] # every leg costs what its pump gives, except the last, one litre short
print(f"{n:,} pumps, one litre short in all: {start_by_trying_each([1] * n, cost)[1]:,} legs driven") Output
the six-pump ring: (2, 8) gain [1, 2, 3], cost [2, 3, 2]: (-1, 5) 1,000 pumps, one litre short in all: 500,500 legs driven 2,000 pumps, one litre short in all: 2,001,000 legs driven 4,000 pumps, one litre short in all: 8,002,000 legs driven
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 every_start.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
The six-pump ring costs 8 legs before pump 2 gets round. On a ring where each pump gives exactly what its leg costs, except for one leg a litre short, every start drives all the way to that leg: 500,500 legs for 1,000 pumps, 8,002,000 for 4,000, and about 5 × 10¹¹ for a million, far more than a time limit of a second or two allows.
What to notice
- The total decides whether any start works. If the pumps give less fuel than the legs cost, every start runs dry somewhere on the loop.
- One failure rules out a whole stretch of starts. A van that started at pump s and runs dry on the leg out of pump j shows that every start from s to j fails too: each of them would skip a part of the drive that never left the tank below zero.
The one-pass scan, step by step Pro: opens with a Pro, Premium or Ultimate pass
In this part:
- Why restarting after a failure is safe: the proof
- Why a start exists whenever the total is not short: the proof
- The scan returns the smallest good start
- A dry run on the six-pump ring
- Every good start, and the fuel a fixed start needs
- The code in five languages
- Checked against trying every start on 1,000 random rings
- Four bugs and a 32-bit total, each with its smallest failing ring
- Edge cases
- Variants
- Exercise: where the van should start
- Where a delivery van should start its ring road
- A practice set with sample answers
- Check yourself
- Interview questions
- In the gas station scan, why may you skip every pump between the current start and the pump on whose leg the tank went below zero?
- A route planner asks the same ring, again and again, whether a given pump is a good start, and between questions single pumps change how much fuel they give. How do you answer each question quickly, and what does each change cost?
- The van's tank now holds at most C litres, and fuel beyond that is lost at the pump. Does the one-pass scan still find a start? Show what breaks, and give a correct method with its cost.
- The van may now drive the loop either way round and must come back to its start. How do you find the smallest pump that works in at least one direction? Say which leg each pump pairs with when the van drives the other way.
- Key takeaways
1 diagram · 6 runnable examples (C++, Go, Java, JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 23 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
References
- greedy algorithm (Dictionary of Algorithms and Data Structures) (National Institute of Standards and Technology (NIST))
- itertools.accumulate (The Python Standard Library) (Python Software Foundation)
- Numeric types: int, float, complex (The Python Standard Library) (Python Software Foundation)
- ECMAScript Language Specification: Number.MAX_SAFE_INTEGER (Ecma International (TC39))
- The Java Language Specification, Java SE 25: 4.2.1 Integral types and values (Oracle)
- The Java Language Specification, Java SE 25: 4.2.2 Integer operations (Oracle)
- Fixed width integer types (C++ reference) (cppreference.com)
- The Go Programming Language Specification: numeric types (The Go Authors)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress