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.