Problem overview
There are n gas stations on a circular route. Station i gives gas[i] units, and driving from station i to the next one costs cost[i]. After station n - 1 the route continues at station 0. The tank has no size limit and starts empty. At each station you take all its gas, then drive on, and a stretch is allowed only if the tank holds at least its cost.
The method CanCompleteCircuit returns the index of the station to start from so that a full lap works, or -1 if no start does. At most one start works.
In the first example, gas = {1, 2, 3, 4, 5} and cost = {3, 4, 5, 1, 2}. Starting at station 3 the tank holds 4, then 8 at station 4, 7 at station 0, 6 at station 1, 5 at station 2, and exactly the 5 needed for the last stretch. The answer is 3. In the second example the gas totals 9 and the lap costs 10, so the answer is -1.
The lesson asks for O(n) time and O(1) extra space.
Approach: one lap decides, one pass finds the start
Trying every station as a start and simulating a lap is O(n²). The greedy solution needs two observations.
The total decides whether any start exists. Add up gas[i] - cost[i] over all stations. If the sum is negative, the lap costs more than all the gas there is, so no start works and the answer is -1. If it isn't negative, a start exists, and the problem says it is unique.
A failure rules out a whole stretch of candidates. Drive from a candidate start, keeping the tank. If the tank drops below 0 on the stretch after station i, the car could not have driven it. Every station between start and i is ruled out too, because the car reached each of them with 0 or more gas. Starting at one of them means being there with an empty tank, which is never better, so the tank would still go negative at i. The next candidate is i + 1, with an empty tank.
After one pass, the last candidate is the answer. It reaches the end of the list without running dry. The stretches skipped before it lose gas overall, but the total isn't negative, so the gas left at the end covers what all those stretches lose, and the car gets back around to the start.
Tracing the pass
For gas = {1, 2, 3, 4, 5} and cost = {3, 4, 5, 1, 2}, this is the tank and the candidate after each station:
i=0 tank=0 start=1 (ran dry, start moves)
i=1 tank=0 start=2 (ran dry, start moves)
i=2 tank=0 start=3 (ran dry, start moves)
i=3 tank=3 start=3
i=4 tank=6 start=3
Stations 0, 1 and 2 each fail at once (the differences are -2, -2 and -2), and the candidate moves to 3. From station 3 the tank never goes negative. For gas = {5, 1, 2, 3, 4} and cost = {4, 4, 1, 5, 1}, the candidate moves to 2 after station 1 and to 4 after station 3, and the answer is 4:
i=0 tank=1 start=0
i=1 tank=0 start=2 (ran dry, start moves)
i=2 tank=1 start=2
i=3 tank=0 start=4 (ran dry, start moves)
i=4 tank=3 start=4
Step by step
- Store the number of stations
gas.Lengthinn. - Create
total, starting at 0, to add up what each station gives minus what the following stretch costs. - Loop
ifrom 0 ton - 1and addgas[i] - cost[i]tototal. A station with less gas than its stretch costs contributes a negative number. - Return -1 if
totalis negative. All the gas can't pay for the lap. Otherwise a start exists. - Create
startat 0 andtankat 0. Station 0 is tried first, and the start only moves forward when it fails. - Loop over the stations again and add
gas[i] - cost[i]totank. Afterwards,tankis the gas left on arriving at the next station. - If
tankis negative, the stretch could not be driven fromstart. Exactly 0 is fine: the car arrives empty and still takes the next station's gas. - Move
starttoi + 1, the first station not yet ruled out, and resettankto 0, because the trip begins again there. - Return
startafter the loop.
The solution in C#
class Solution {
public int CanCompleteCircuit(int[] gas, int[] cost) {
int n = gas.Length;
int total = 0;
for (int i = 0; i < n; i++) {
total += gas[i] - cost[i];
}
if (total < 0) {
return -1;
}
int start = 0;
int tank = 0;
for (int i = 0; i < n; i++) {
tank += gas[i] - cost[i];
if (tank < 0) {
start = i + 1;
tank = 0;
}
}
return start;
}
}
The C# details worth knowing:
- Arrays.
gas.Lengthis a property and gives the array's fixed size. The two arrays have the same length, so one loop bound serves both. - Overflow.
intarithmetic wraps silently in C# unless the code is in acheckedcontext. Here it can't happen: with up to 100,000 stations and values up to 10,000, the largest possible sum of gas is 1,000,000,000, belowint.MaxValue(2,147,483,647). With 100,000 stations of 10,000 gas and cost 0, the method returns 0 and the sum is 1000000000. - Compound assignment.
total += gas[i] - cost[i]adds the difference in place, and the subtraction happens first. - Two loops, two scopes. Both loops declare
int i. That is allowed because eachiexists only inside its ownfor. - The candidate stays a valid index. When
totalis not negative,startnever ends up equal ton: that would mean every stretch of the lap had failed in turn, which would make the total negative.
Complexity
Time: O(n). Two loops over the stations, each doing constant work per iteration. No station is revisited, because the candidate only moves forward.
Space: O(1). Four integers: n, total, start and tank.
Edge cases and common mistakes
- A single station.
gas = {5}andcost = {4}returns 0, andgas = {4}withcost = {5}returns -1. When gas equals cost, as in{3}and{3}, the answer is 0. - Everything zero.
gas = {0, 0}andcost = {0, 0}returns 0: the total is 0, so a start exists, and nothing ever makes the tank negative. - A tank of exactly 0 is fine. Using
tank <= 0as the failure test would move the start wrongly. Forgas = {3}andcost = {3}it would turn the start into 1, which is not even a station. - Skipping the total check. Without it, the second loop returns some index even when no start works. For
gas = {2, 3, 4}andcost = {3, 4, 3}the correct answer is -1. - Forgetting to reset the tank. The new start begins empty. Carrying the negative tank over would reject good starts.
- Restarting at
iinstead ofi + 1. Stationiis where the car just failed to leave, so it can't be the next candidate. - Testing each start with a full lap. It works but takes
O(n²), too slow for 100,000 stations.
Testing
gas = {1, 2, 3, 4, 5},cost = {3, 4, 5, 1, 2}gives3.gas = {2, 3, 4},cost = {3, 4, 3}gives-1.gas = {5, 1, 2, 3, 4},cost = {4, 4, 1, 5, 1}gives4.gas = {5},cost = {4}gives0.gas = {4},cost = {5}gives-1.gas = {0, 5},cost = {3, 2}gives1.
