Gas Station
Gas Station
You have n gas stations arranged in a circle. At station i, you can take gas[i] units of gas, and it costs cost[i] units to travel to station i+1 (wrapping around).
Return the starting station index if you can complete the circuit once, otherwise return -1.
A brute-force start check is O(n^2). A greedy scan can solve it in O(n).
Why this is worth learning
Total balance decides existence, while a failed candidate lets you discard an entire block of starts. Feasibility does not imply uniqueness; several starts can work.
Before coding: one-minute experiment
For gas=[2,2] and cost=[1,1], how many valid starts are there, and which does the supplied scan return?
Function signature
func CanCompleteCircuit(gas []int, cost []int) int
Inputs / outputs
gas[i]: gas available at stationi.cost[i]: gas required to move from stationitoi+1.- Return: starting index (0-based) or
-1.
Example
gas = [1,2,3,4,5]
cost = [3,4,5,1,2]
Output: 3
Start at 3: 4->5->1->2->3 works.
Constraints
1 <= len(gas) == len(cost) <= 100_0000 <= gas[i], cost[i] <= 1_000_000_000
Core idea (near-solution)
Two facts:
- If total gas < total cost, no solution exists.
- If total gas >= total cost, there is at least one valid start. Uniqueness does not follow from the total check: gas=[1,1], cost=[1,1] permits both starts. The reference preserves its deterministic first surviving candidate.
Greedy scan:
- Track
tank= current gas in the tank. - If
tankdrops below 0 at stationi, then no station from the current start up toican be a valid start. Reset the start toi+1and clear the tank.
Invariant: when scanning, tank represents the net gas since the last candidate start.
Algorithm (step-by-step)
- Compute
totalGasandtotalCost. IftotalGas < totalCost, return-1. - Initialize
start = 0,tank = 0. - For each station
ifrom 0 ton-1:tank += gas[i] - cost[i].- If
tank < 0, setstart = i+1and resettank = 0.
- Return
start.
Correctness sketch
Let d[i]=gas[i]-cost[i]. Suppose a candidate s first becomes negative at i. Every earlier prefix from s to j-1, for s<=j<=i, was nonnegative. Hence sum(d[j:i+1])=sum(d[s:i+1])-sum(d[s:j]) is negative too, so every start in [s,i] fails before or at i. Resetting to i+1 discards no feasible start. Equivalently, the final candidate lies immediately after a strict minimum of the prefix sums. If the total sum is nonnegative, all suffix and wrapped-prefix balances from that minimum are nonnegative, proving this candidate completes the circuit. A negative total makes every start impossible.
Trace (hand walkthrough)
Example input:
gas = [1,2,3,4,5]
cost = [3,4,5,1,2]
Output: 3
Start at 3: 4->5->1->2->3 works.
Walk:
- diffs = gas-cost = [-2,-2,-2,3,3], total=0 so possible
- i=0 tank=-2 -> reset start=1, tank=0
- i=1 tank=-2 -> reset start=2, tank=0
- i=2 tank=-2 -> reset start=3, tank=0
- i=3 tank=3; i=4 tank=6 -> end with start=3
Pitfalls to avoid
- Forgetting the total gas check before the greedy scan.
- Resetting
starttoo late (must reset immediately whentank < 0).
Debugging lens
Test a reset near the end, a zero total, and multiple feasible starts. Do not reset when tank equals zero: the current candidate remains feasible.
Complexity
- Time:
O(n) - Extra space:
O(1)
Implementation notes (Go)
- The full domain requires the course's 64-bit Go target: totals can reach 100_000 × 1_000_000_000 = 10^14. An int32 accumulator would overflow even if the returned station index is small.
Reconstruction checkpoint
For gas=[2,2] and cost=[1,1], how many valid starts are there, and which does the supplied scan return?
Model answer
Both 0 and 1 work. The scan never resets and returns 0. This is a direct counterexample to a claim of unique feasibility.