title: Gas Station
slug: gas-station
difficulty: medium
leetcode_id: 134
leetcode_url: https://leetcode.com/problems/gas-station/
categories:
- arrays
patterns:
- greedy
function_signature: "def can_complete_circuit(gas: list[int], cost: list[int]) -> int:"
test_cases:
visible:
- input: { gas: [1, 2, 3, 4, 5], cost: [3, 4, 5, 1, 2] }
expected: 3
- input: { gas: [2, 3, 4], cost: [3, 4, 3] }
expected: -1
hidden:
- input: { gas: [5, 1, 2, 3, 4], cost: [4, 4, 1, 5, 1] }
expected: 4
- input: { gas: [3], cost: [3] }
expected: 0
- input: { gas: [1, 2], cost: [2, 1] }
expected: 1
- input: { gas: [5, 8, 2, 8], cost: [6, 5, 6, 6] }
expected: 3
- input: { gas: [3, 3, 4], cost: [3, 4, 4] }
expected: -1
description: |
There are `n` gas stations along a circular route, where the amount of gas at the ith station is `gas[i]`.
You have a car with an unlimited gas tank and it costs `cost[i]` of gas to travel from the ith station to its next (i + 1)th station. You begin the journey with an empty tank at one of the gas stations.
Given two integer arrays `gas` and `cost`, return *the starting gas station's index if you can travel around the circuit once in the clockwise direction, otherwise return* `-1`. If there exists a solution, it is **guaranteed** to be **unique**.
constraints: |
- `n == gas.length == cost.length`
- `1 <= n <= 10^5`
- `0 <= gas[i], cost[i] <= 10^4`
- The input is generated such that the answer is unique
examples:
- input: "gas = [1,2,3,4,5], cost = [3,4,5,1,2]"
output: "3"
explanation: "Start at station 3 (index 3) and fill up with 4 units of gas. Your tank = 0 + 4 = 4. Travel to station 4: tank = 4 - 1 + 5 = 8. Travel to station 0: tank = 8 - 2 + 1 = 7. Travel to station 1: tank = 7 - 3 + 2 = 6. Travel to station 2: tank = 6 - 4 + 3 = 5. Travel to station 3: cost is 5, gas is just enough. Return 3."
- input: "gas = [2,3,4], cost = [3,4,3]"
output: "-1"
explanation: "Starting from any station, you cannot complete the circuit. For example, starting at station 2 with 4 gas, you can reach station 1 but cannot travel back to station 2 (requires 4 gas, you only have 3)."
explanation:
intuition: |
Imagine you're planning a road trip around a circular route with gas stations. At each station, you can fill up some gas, but travelling to the next station costs some gas. The question is: **can you find a starting point where you never run out of fuel?**
The key insight comes from two observations:
**Observation 1: Total gas must be enough.** If the total gas available across all stations is less than the total cost to travel the entire circuit, it's impossible to complete the trip from *any* starting point. Conversely, if total gas >= total cost, a solution is **guaranteed** to exist.
**Observation 2: If you fail at station `j`, skip all previous candidates.** Suppose you start at station `i` and run out of gas at station `j`. You might think: "Maybe I should try starting at `i+1`?" But here's the crucial insight — if you couldn't reach `j` starting from `i` with a full journey's worth of gas from stations `i` to `j-1`, then starting from any station *between* `i` and `j` would give you even *less* gas (you'd miss the contributions from earlier stations). So **all stations from `i` to `j` are invalid starting points**.
This means when we fail, we can jump our candidate start directly to `j+1`, making this a linear-time algorithm.
approach: |
We solve this using a **Single Pass Greedy Approach**:
**Step 1: Initialise tracking variables**
- `total_tank`: Tracks the cumulative surplus/deficit across all stations (used to check if a solution exists)
- `current_tank`: Tracks the current fuel level from our candidate starting point
- `start_station`: The index of our current candidate starting point, initialised to `0`
**Step 2: Iterate through each station**
- For each station `i`, calculate `gas[i] - cost[i]` (the net gain/loss at this station)
- Add this value to both `total_tank` and `current_tank`
- If `current_tank` becomes negative, it means we can't reach station `i+1` from our current `start_station`
- When this happens, reset `start_station` to `i+1` and reset `current_tank` to `0`
**Step 3: Check feasibility and return**
- After the loop, if `total_tank >= 0`, a solution exists and `start_station` is our answer
- If `total_tank < 0`, return `-1` (not enough total gas)
The greedy choice — skipping all stations between our failed start and the failure point — is valid because those intermediate stations would only give us less fuel to work with.
common_pitfalls:
- title: Trying Every Starting Point
description: |
A brute force approach would try starting at each station and simulate the entire trip:
- For each starting station `i`, simulate travelling all `n` stations
- Check if the tank ever goes negative
This results in **O(n^2) time complexity**. With `n` up to `10^5`, this means up to 10 billion operations — a guaranteed **Time Limit Exceeded (TLE)**.
wrong_approach: "Nested loops simulating from each start"
correct_approach: "Single pass with smart candidate elimination"
- title: Not Understanding Why We Can Skip Stations
description: |
When you fail at station `j` starting from station `i`, it might seem wasteful to skip directly to `j+1`. Why not try `i+1`?
The reason is mathematical: if you reached stations `i+1`, `i+2`, ..., `j-1` with non-negative fuel (otherwise you would have failed earlier), but still failed at `j`, then starting at any of those intermediate stations means you'd have *less* accumulated fuel when you reach `j`.
For example, if stations give net gains of `[+3, -1, -1, -2]` and you fail at index 3 starting from index 0, starting at index 1 means you miss the +3 from station 0, making failure even more certain.
wrong_approach: "Increment start by 1 when failing"
correct_approach: "Jump start to failure_point + 1"
- title: Forgetting to Check Total Feasibility
description: |
Just finding a valid `start_station` candidate isn't enough. You must verify that the **total gas >= total cost** for the entire circuit.
The `total_tank` variable serves this purpose. Even if we find a candidate, if `total_tank < 0` at the end, no solution exists.
wrong_approach: "Only tracking current_tank"
correct_approach: "Track both current_tank and total_tank"
key_takeaways:
- "**Greedy elimination**: When a candidate fails, use problem structure to eliminate multiple candidates at once, not just one"
- "**Global vs local tracking**: Use separate variables for local decisions (`current_tank`) and global feasibility (`total_tank`)"
- "**Circular problems**: Often can be solved with a single linear pass by tracking cumulative state"
- "**Proof intuition**: If total resources >= total cost, a valid starting point must exist — this is a key insight for many resource allocation problems"
time_complexity: "O(n). We traverse both arrays exactly once, performing constant-time operations at each station."
space_complexity: "O(1). We only use three integer variables (`total_tank`, `current_tank`, `start_station`) regardless of input size."
solutions:
- approach_name: Single Pass Greedy
is_optimal: true
code: |
def can_complete_circuit(gas: list[int], cost: list[int]) -> int:
# Track total surplus to check if solution exists
total_tank = 0
# Track current surplus from candidate start
current_tank = 0
# Our candidate starting station
start_station = 0
for i in range(len(gas)):
# Net gain/loss at this station
net = gas[i] - cost[i]
total_tank += net
current_tank += net
# If we can't reach the next station from current start
if current_tank < 0:
# All stations from start to i are invalid
# Try starting from the next station
start_station = i + 1
current_tank = 0
# If total gas >= total cost, solution exists at start_station
# Otherwise, impossible to complete the circuit
return start_station if total_tank >= 0 else -1
explanation: |
**Time Complexity:** O(n) — Single pass through both arrays.
**Space Complexity:** O(1) — Only three integer variables used.
The key insight is that if we fail to reach station `j` from station `i`, all stations between `i` and `j` are also invalid starting points. Combined with tracking total feasibility, this gives us an elegant linear solution.
- approach_name: Brute Force
is_optimal: false
code: |
def can_complete_circuit(gas: list[int], cost: list[int]) -> int:
n = len(gas)
# Try each station as a starting point
for start in range(n):
tank = 0
can_complete = True
# Simulate travelling around the circuit
for i in range(n):
# Current station index (wrapping around)
station = (start + i) % n
# Fill up and travel to next station
tank += gas[station] - cost[station]
# Ran out of gas before reaching next station
if tank < 0:
can_complete = False
break
if can_complete:
return start
return -1
explanation: |
**Time Complexity:** O(n^2) — For each of n starting points, we simulate travelling n stations.
**Space Complexity:** O(1) — Only tracking tank and loop variables.
This approach is correct but inefficient. It tries every possible starting station and simulates the full circuit. With n up to 10^5, this will cause TLE. Included to illustrate why the greedy optimisation is necessary.