Gas Station
Difficulty: Medium Source: NeetCode
Problem
There are
ngas stations in a circular route. At stationi, you getgas[i]fuel and it costscost[i]to travel to the next station. You start with an empty tank. Find the starting station index from which you can complete the circuit, or return-1if no solution exists. The answer is guaranteed to be unique if it exists.Example 1: Input:
gas = [1,2,3,4,5],cost = [3,4,5,1,2]Output:3Example 2: Input:
gas = [2,3,4],cost = [3,4,3]Output:-1Constraints:
1 <= n <= 10^50 <= gas[i], cost[i] <= 10^4
Prerequisites
Before attempting this problem, you should be comfortable with:
- Greedy algorithms — key insight about when to abandon a starting position
- Prefix sums — understanding running surplus/deficit
1. Brute Force
Intuition
Try each station as the starting point. Simulate the full circuit from that station. If at any point the tank goes negative, abort and try the next start. If we complete the full loop, return the start index.
Algorithm
- For each start
ifrom0ton-1:- Set
tank = 0. - Simulate going through
nstations (wrapping with modulo). - If tank goes negative at any point, break.
- If we complete
nsteps, returni.
- Set
- Return
-1.
Solution
def canCompleteCircuit(gas, cost):
n = len(gas)
for start in range(n):
tank = 0
can_complete = True
for step in range(n):
i = (start + step) % n
tank += gas[i] - cost[i]
if tank < 0:
can_complete = False
break
if can_complete:
return start
return -1
print(canCompleteCircuit([1, 2, 3, 4, 5], [3, 4, 5, 1, 2])) # 3
print(canCompleteCircuit([2, 3, 4], [3, 4, 3])) # -1
print(canCompleteCircuit([5], [4])) # 0
Complexity
- Time:
O(n²) - Space:
O(1)
2. Greedy — Single Pass
Intuition
Two key observations:
- Feasibility: if
sum(gas) < sum(cost), it’s impossible — return-1. - Finding the start: if we start from some station and the cumulative surplus (
tank) goes negative at stationk, then no starting station from the original start up tokcan work. Why? Because we already had a positive running sum when passing through those intermediate stations — if they were the start, they’d also fail by stationk. So we skip ahead and tryk + 1as the new candidate.
A single pass tracks total_surplus (overall feasibility) and tank (current candidate’s viability).
Algorithm
total = 0,tank = 0,start = 0.- For each station
i:net = gas[i] - cost[i].total += net,tank += net.- If
tank < 0: resetstart = i + 1,tank = 0.
- If
total < 0, return-1. - Return
start.
Solution
def canCompleteCircuit(gas, cost):
total = 0
tank = 0
start = 0
for i in range(len(gas)):
net = gas[i] - cost[i]
total += net
tank += net
if tank < 0:
# Can't start from 'start' through i; try i+1
start = i + 1
tank = 0
# If overall surplus is negative, it's impossible
return start if total >= 0 else -1
print(canCompleteCircuit([1, 2, 3, 4, 5], [3, 4, 5, 1, 2])) # 3
print(canCompleteCircuit([2, 3, 4], [3, 4, 3])) # -1
print(canCompleteCircuit([5], [4])) # 0
print(canCompleteCircuit([3, 3, 4], [3, 4, 4])) # -1
print(canCompleteCircuit([1, 2, 3, 4, 5], [2, 3, 4, 3, 3])) # 0
Complexity
- Time:
O(n) - Space:
O(1)
Common Pitfalls
Not checking total feasibility. The greedy always produces a start candidate, but that candidate is only valid if sum(gas) >= sum(cost). Always check total >= 0 at the end.
Setting start = i instead of start = i + 1. When we fail at station i, station i itself can’t be part of a valid prefix — we start fresh at i + 1.
Resetting total along with tank. Only reset tank when the candidate fails. total accumulates over the whole circuit to check overall feasibility — never reset it.