LeetCode 134 - Gas Station
The problem describes a circular route containing n gas stations. At each station i, you can collect gas[i] units of fuel. Traveling from station i to station (i + 1) % n consumes cost[i] units of fuel.
Difficulty: 🟡 Medium
Topics: Array, Greedy
Solution
Problem Understanding
The problem describes a circular route containing n gas stations. At each station i, you can collect gas[i] units of fuel. Traveling from station i to station (i + 1) % n consumes cost[i] units of fuel.
You must determine whether there exists a starting station such that a car beginning with an empty tank can complete one full loop around the circle without ever running out of gas. If such a station exists, the problem guarantees that the answer is unique.
The input consists of two arrays:
gas[i]represents how much fuel you gain at stationicost[i]represents how much fuel is needed to travel from stationito the next station
The expected output is:
- The index of the valid starting station if completing the circuit is possible
-1if no valid starting station exists
The constraints are important:
1 <= n <= 10^50 <= gas[i], cost[i] <= 10^4
Since n can be as large as 100,000, any solution worse than linear or near-linear time is likely too slow. This immediately rules out expensive nested simulations for every station.
Several edge cases are important:
- The total gas across all stations may be less than the total cost, making completion impossible
- Some stations may individually fail immediately, even if the overall route is possible
- Arrays of length
1must still work correctly - Gas and cost values can both be zero
- The valid start may appear late in the array after many failed attempts
The problem also guarantees uniqueness of the answer when a solution exists, which simplifies reasoning about the optimal solution.
Approaches
Brute Force Approach
The most direct solution is to try every station as a starting point.
For each starting index:
- Begin with an empty tank
- Simulate traveling around the entire circuit
- At every station:
- Add available gas
- Subtract travel cost
- If the tank ever becomes negative, this starting station fails
- If we successfully return to the start, return that index
This approach is correct because it explicitly checks every possible starting position. However, it is inefficient because each simulation may traverse nearly the entire array.
In the worst case:
- There are
nstarting stations - Each simulation costs
O(n)
This leads to O(n^2) time complexity, which is too slow for n = 10^5.
Optimal Greedy Approach
The key insight comes from understanding failure behavior.
Suppose we start at station A and fail at station B. This means that somewhere between A and B, the cumulative gas became insufficient.
Now consider any station between A and B as a potential starting point. None of them can succeed either.
Why?
Because starting later would only give us less gas before reaching the failure point. If the earlier start could not accumulate enough fuel, a later start cannot do better.
This observation allows us to skip many stations at once.
Another critical observation is:
- If total gas across all stations is less than total cost, completing the circuit is impossible regardless of starting point
So the algorithm becomes:
- Check whether total gas is at least total cost
- Traverse the array once
- Maintain current fuel balance
- Whenever balance becomes negative:
- Reset balance to zero
- Set next station as the new candidate start
This works because any station before the failure point has already been proven invalid.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n²) | O(1) | Simulates the full trip from every station |
| Optimal | O(n) | O(1) | Uses greedy skipping based on failure intervals |
Algorithm Walkthrough
Optimal Greedy Algorithm
- Initialize two variables:
total_balance, tracks total gas minus total cost across the entire routecurrent_balance, tracks gas remaining while testing the current candidate start
- Initialize
start_index = 0. - Traverse every station from left to right.
- At each station:
- Compute
difference = gas[i] - cost[i] - Add this value to both
total_balanceandcurrent_balance
- If
current_balancebecomes negative:
- The current starting station cannot work
- Any station between the current start and this failure point also cannot work
- Set
start_index = i + 1 - Reset
current_balance = 0
- Continue scanning the remaining stations.
- After processing all stations:
- If
total_balance < 0, return-1 - Otherwise, return
start_index
Why it works
The algorithm relies on a greedy invariant:
If starting from station A causes failure at station B, then no station between A and B can be a valid starting point.
This is because all intermediate stations would begin with less accumulated fuel than starting at A.
Therefore, once failure occurs, we can safely skip every station in that interval and move directly to B + 1.
The final candidate works because the total gas is sufficient to cover the total cost globally.
Python Solution
from typing import List
class Solution:
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
total_balance = 0
current_balance = 0
start_index = 0
for i in range(len(gas)):
difference = gas[i] - cost[i]
total_balance += difference
current_balance += difference
if current_balance < 0:
start_index = i + 1
current_balance = 0
if total_balance < 0:
return -1
return start_index
The implementation closely follows the greedy algorithm.
total_balance determines whether the journey is globally possible. If the total gas available is smaller than the total travel cost, no solution exists.
current_balance represents the remaining fuel while evaluating the current candidate starting station. As we iterate through the stations, we continuously update this value.
Whenever current_balance becomes negative, we know the current starting point cannot succeed. More importantly, all stations between the current start and the failure point are also invalid. Therefore, we move the candidate start to the next station and reset the running balance.
At the end of the traversal, if the overall balance is non-negative, the stored start_index is guaranteed to be the unique valid answer.
Go Solution
func canCompleteCircuit(gas []int, cost []int) int {
totalBalance := 0
currentBalance := 0
startIndex := 0
for i := 0; i < len(gas); i++ {
difference := gas[i] - cost[i]
totalBalance += difference
currentBalance += difference
if currentBalance < 0 {
startIndex = i + 1
currentBalance = 0
}
}
if totalBalance < 0 {
return -1
}
return startIndex
}
The Go implementation mirrors the Python logic almost exactly.
Go uses explicit variable declarations and integer arithmetic with int. Since the constraints are relatively small, integer overflow is not a concern here.
Slices are used directly, and no extra memory allocation is required. The algorithm remains fully iterative and constant-space.
Worked Examples
Example 1
gas = [1,2,3,4,5]
cost = [3,4,5,1,2]
We compute the difference at each station:
| Index | Gas | Cost | Difference | Current Balance | Total Balance | Start Index |
|---|---|---|---|---|---|---|
| 0 | 1 | 3 | -2 | -2 | -2 | 0 |
| Reset | 0 | -2 | 1 | |||
| 1 | 2 | 4 | -2 | -2 | -4 | 1 |
| Reset | 0 | -4 | 2 | |||
| 2 | 3 | 5 | -2 | -2 | -6 | 2 |
| Reset | 0 | -6 | 3 | |||
| 3 | 4 | 1 | 3 | 3 | -3 | 3 |
| 4 | 5 | 2 | 3 | 6 | 0 | 3 |
Final values:
total_balance = 0start_index = 3
Since total balance is non-negative, station 3 is the answer.
Example 2
gas = [2,3,4]
cost = [3,4,3]
| Index | Gas | Cost | Difference | Current Balance | Total Balance | Start Index |
|---|---|---|---|---|---|---|
| 0 | 2 | 3 | -1 | -1 | -1 | 0 |
| Reset | 0 | -1 | 1 | |||
| 1 | 3 | 4 | -1 | -1 | -2 | 1 |
| Reset | 0 | -2 | 2 | |||
| 2 | 4 | 3 | 1 | 1 | -1 | 2 |
Final values:
total_balance = -1
Since total gas is smaller than total cost, completing the circuit is impossible.
Return -1.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Each station is processed exactly once |
| Space | O(1) | Only a few integer variables are used |
The algorithm performs a single linear scan across the arrays. No nested loops or additional data structures are needed.
Although candidate starting stations change during traversal, each index is still visited only once, giving true linear complexity.
Space usage remains constant because the algorithm stores only running totals and indices.
Test Cases
from typing import List
class Solution:
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
total_balance = 0
current_balance = 0
start_index = 0
for i in range(len(gas)):
difference = gas[i] - cost[i]
total_balance += difference
current_balance += difference
if current_balance < 0:
start_index = i + 1
current_balance = 0
if total_balance < 0:
return -1
return start_index
solution = Solution()
assert solution.canCompleteCircuit([1,2,3,4,5], [3,4,5,1,2]) == 3
# Standard example with valid solution
assert solution.canCompleteCircuit([2,3,4], [3,4,3]) == -1
# Standard example with impossible route
assert solution.canCompleteCircuit([5], [4]) == 0
# Single station, enough gas
assert solution.canCompleteCircuit([2], [3]) == -1
# Single station, insufficient gas
assert solution.canCompleteCircuit([0,0,0], [0,0,0]) == 0
# All zeros
assert solution.canCompleteCircuit([3,1,1], [1,2,2]) == 0
# Valid start at index 0
assert solution.canCompleteCircuit([1,2,3], [3,1,2]) == 1
# Valid start after early failure
assert solution.canCompleteCircuit([6,1,4,3,5], [3,8,2,4,2]) == 2
# Multiple resets before finding solution
assert solution.canCompleteCircuit([4,5,2,6,5,3], [3,2,7,3,2,9]) == -1
# Total gas less than total cost
assert solution.canCompleteCircuit([10,1,1,1], [1,3,3,3]) == 0
# Large surplus at first station
| Test | Why |
|---|---|
[1,2,3,4,5], [3,4,5,1,2] |
Standard successful case |
[2,3,4], [3,4,3] |
Standard impossible case |
[5], [4] |
Single-element success |
[2], [3] |
Single-element failure |
[0,0,0], [0,0,0] |
Zero values everywhere |
[3,1,1], [1,2,2] |
Start index is zero |
[1,2,3], [3,1,2] |
Requires resetting candidate start |
[6,1,4,3,5], [3,8,2,4,2] |
Multiple failed intervals |
[4,5,2,6,5,3], [3,2,7,3,2,9] |
Global impossibility check |
[10,1,1,1], [1,3,3,3] |
Large early surplus |
Edge Cases
Total Gas Is Less Than Total Cost
A common mistake is attempting to simulate routes without first checking whether the journey is globally possible.
For example:
gas = [2,3,4]
cost = [3,4,5]
The total gas is 9, while the total cost is 12. No starting point can ever succeed.
The implementation handles this naturally using total_balance. If the final balance is negative, the algorithm immediately returns -1.
Single Station Arrays
Arrays of length 1 are easy to mishandle due to circular traversal assumptions.
For example:
gas = [5]
cost = [4]
The answer should be 0 because the car can leave and return to the same station successfully.
The algorithm works correctly because the loop processes the single station normally, and the balance remains non-negative.
Multiple Consecutive Failed Starts
Consider:
gas = [1,2,3,4]
cost = [3,4,1,2]
Starting at index 0 fails immediately. Starting at 1 also fails.
A naive implementation might retry every station individually, leading to quadratic complexity.
The greedy solution avoids this inefficiency by proving that once a segment fails, every station within that failed segment is invalid. It skips directly to the next possible candidate, preserving linear runtime.