Start learning free

LESSON 463 OF 540 · GREEDY

Gas Station in TypeScript: O(n) Greedy with O(1) Space

Practice O(n) Read the full explanation

There are n gas stations on a circular route, numbered from 0 to n - 1. Station i has gas[i] units of gas, and driving from station i to the next station costs cost[i] units. After the last station, n - 1, the next one is station 0 again.

Your car's tank has no size limit, and it starts empty at one of the stations. At every station you take all of its gas, then drive on to the next station, going around the circle in increasing order. You can drive a stretch only if the tank holds at least its cost.

Return the index of the station to start from so that you drive all the way around the circle once and get back to it, or -1 if no start works. When a start works, it is the only one.

 

Example 1:

Input: gas = [1, 2, 3, 4, 5], cost = [3, 4, 5, 1, 2]
Output: 3
Explanation: Start at station 3 and take its 4 units: the tank has 4.
Drive to station 4 (cost 1) and take 5: 4 - 1 + 5 = 8.
Drive to station 0 (cost 2) and take 1: 8 - 2 + 1 = 7.
Drive to station 1 (cost 3) and take 2: 7 - 3 + 2 = 6.
Drive to station 2 (cost 4) and take 3: 6 - 4 + 3 = 5.
Drive back to station 3: it costs 5, and the tank has exactly 5.
From any other start, the tank runs dry on the way.

Example 2:

Input: gas = [2, 3, 4], cost = [3, 4, 3]
Output: -1
Explanation: The stations have 2 + 3 + 4 = 9 units of gas in all, but the trip around
costs 3 + 4 + 3 = 10, so no start can work.

Example 3:

Input: gas = [5, 1, 2, 3, 4], cost = [4, 4, 1, 5, 1]
Output: 4
Explanation: From station 0 you reach station 1 with 5 - 4 = 1, but 1 + 1 = 2 is less than
the 4 it costs to go on. Starting at station 1 instead is no better: you would be there
with an empty tank instead of 1. From station 2 you reach station 3 with 2 - 1 = 1, but
1 + 3 = 4 is less than 5, and station 3 can't go on alone either. From station 4 the tank
has 3 at station 0, 4 at station 1, 1 at station 2, 2 at station 3 and 0 back at station 4.

 

Constraints:

  • n == gas.length == cost.length
  • 1 <= n <= 105
  • 0 <= gas[i], cost[i] <= 104
  • At most one station works as the start.

Note:

Your algorithm should run in O(n) time and use O(1) extra space.

The tutorial is greedy. First it adds up gas[i] - cost[i] over all the stations: if the total is negative, the trip around costs more than all the gas there is, and no start can work. Otherwise it drives once from station 0 to the last station, keeping the gas in the tank and a candidate start, first 0. Whenever the tank drops below 0 on the stretch after station i, the car couldn't have driven it: the candidate moves to station i + 1, and the tank starts again from empty. At the end, the candidate is the answer.

Why skipping all the stations from start to i is safe: starting at start, the car reached each of them with 0 or more gas in the tank. Starting at one of them instead means being there with an empty tank, which is never better, so the tank would still drop below 0 after station i. None of them can be the start, and the next station to try is i + 1.

Why the last candidate works: from it, the car reaches the end of the list without running dry. Each stretch it skipped before it loses gas overall, but the total isn't negative, so the gas left in the tank at the end is at least what all of them lose together. Inside a skipped stretch the tank only goes below 0 at its very end, so with that gas the car gets through each of them, and back to the start.

STEP 1 OF 13
    Ctrl + Enter to check

    TYPESCRIPT · GREEDY

    Gas Station in TypeScript, explained

    Problem overview

    There are n gas stations on a circular route. Station i gives gas[i] units of gas, and driving from station i to the next one costs cost[i]. After station n - 1 comes station 0 again. The tank has no size limit and starts empty. At each station you take all its gas, then drive on, and you can drive a stretch only if the tank holds at least its cost.

    The method canCompleteCircuit(gas, cost) returns the index of the station to start from so that you drive once around the circle and get back to it, or -1 if no start works. At most one start works.

    For gas = [1, 2, 3, 4, 5] and cost = [3, 4, 5, 1, 2] the answer is 3. Starting there you take 4, and the tank holds 8 after station 4, then 7, 6 and 5 at stations 0, 1 and 2. The last stretch, back to station 3, costs 5, which is exactly what is in the tank. For gas = [2, 3, 4] and cost = [3, 4, 3] the answer is -1: there are 9 units of gas in all and the trip costs 10.

    Approach: one pass with a moving start

    The brute force tries every start and simulates the whole trip, which takes O(n²). The statement asks for O(n) time and O(1) extra space, so something smarter is needed. Two observations do it.

    First, the totals. Add up gas[i] - cost[i] over all stations. If the total is negative, the trip costs more than all the gas there is, and no start can work. If it isn't negative, a start exists.

    Second, a failed start rules out everything before the failure. Drive from the candidate start, starting at station 0, and keep the gas in tank. If the tank drops below 0 on the stretch after station i, the car can't have driven it. Now take any station between start and i. Starting from the original start, the car reached it with 0 or more gas. Starting there means arriving with an empty tank, which is never better, so the tank would still drop below 0 after station i. None of those stations can be the start, so the next candidate is i + 1 and the tank begins again from empty.

    At the end, the last candidate is the answer. From it the car reaches the end of the list without running dry. The stretches skipped earlier each lose gas overall, but the grand total isn't negative, so the gas left at the end covers them. Inside a skipped stretch the tank goes below 0 only at its very end, so with that gas the car gets through each of them and back to the start.

    For gas = [5, 1, 2, 3, 4] and cost = [4, 4, 1, 5, 1], the second loop goes like this:

    i=0 tank=1 start=0
    i=1 tank=0 start=2
    i=2 tank=1 start=2
    i=3 tank=0 start=4
    i=4 tank=3 start=4
    

    At i = 1 the tank would be -2, so the candidate moves to station 2 and the tank resets to 0. At i = 3 the tank would be -3, so the candidate moves to station 4. The answer is 4.

    Step by step

    1. Store the number of stations in n. The route is a circle, but the loops only walk from 0 to n - 1.
    2. Declare total with let, at 0. It sums what each station gives minus what the stretch after it costs.
    3. Loop over all stations and add gas[i] - cost[i] to total.
    4. If total is negative, return -1. The gas can't pay for the whole trip.
    5. Declare start at 0, the station being tried. It moves forward only when it fails.
    6. Declare tank at 0, the gas left while driving from start.
    7. Loop over the stations once more. One drive from station 0 to the end is enough.
    8. Take station i's gas and drive its stretch: add gas[i] - cost[i] to tank. Afterwards tank is the gas left on arriving at the next station.
    9. Check for a dry tank: if tank < 0, the car couldn't have driven the stretch from start. A tank of exactly 0 is fine, because the next station's gas can still be taken.
    10. Move start to i + 1, the station after the failed stretch, which is the key step.
    11. Empty the tank by setting it to 0, since the trip begins again at the new start.
    12. Return start. The first loop showed that a start exists and the second loop skipped everything that can't be it.

    The solution in TypeScript

    class Solution {
        canCompleteCircuit(gas: number[], cost: number[]): number {
            const n = gas.length;
            let total = 0;
            for (let i = 0; i < n; i++) {
                total += gas[i] - cost[i];
            }
            if (total < 0) {
                return -1;
            }
            let start = 0;
            let tank = 0;
            for (let i = 0; i < n; i++) {
                tank += gas[i] - cost[i];
                if (tank < 0) {
                    start = i + 1;
                    tank = 0;
                }
            }
            return start;
        }
    }
    

    A few TypeScript details:

    • let and const. n never changes, so it is a const. total, start and tank change in the loops, so they are let. Their types, number, are inferred from the starting 0.
    • Numbers are doubles. The sums stay far below 2^53 (at most 10^5 stations of at most 10^4 each), so every addition is exact and there is no overflow.
    • Can start become n? It can't when total isn't negative: if the tank were negative at the last station, every stretch since the previous reset would be negative, and so would the total. So the returned index is always a valid station.

    Complexity

    Time: O(n). Two passes over the stations, each doing constant work per station.

    Space: O(1). The method keeps four numbers: n, total, start and tank.

    Edge cases and common mistakes

    • Skipping the total check. The loop would still return some station, but not -1. For gas = [2, 3, 4] and cost = [3, 4, 3] it returns 2 instead of -1, because the loop on its own can't see the stretches that wrap around the circle.
    • Treating 0 as failure. Use tank < 0, not tank <= 0. A tank of exactly 0 after a stretch is allowed: the car arrives with an empty tank and takes the next station's gas.
    • Resetting start to i. The failed stretch is the one after station i, so the car can't leave station i either, which makes i + 1 the next candidate.
    • Forgetting to reset tank. The new start begins with an empty tank, and leftover debt from the old start would count against it.
    • One station. gas = [5] with cost = [4] returns 0, and gas = [4] with cost = [5] returns -1.

    Testing

    • gas = [1, 2, 3, 4, 5], cost = [3, 4, 5, 1, 2] gives 3.
    • gas = [2, 3, 4], cost = [3, 4, 3] gives -1.
    • gas = [5, 1, 2, 3, 4], cost = [4, 4, 1, 5, 1] gives 4.
    • gas = [5], cost = [4] gives 0.
    • gas = [3], cost = [3] gives 0.
    Keep going with TypeScript500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free