Start learning free

LESSON 459 OF 536 · GREEDY

Gas Station in Java: O(n) Greedy with One Pass

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

    JAVA · GREEDY

    Gas Station in Java, explained

    Problem overview

    There are n stations on a circular route. Station i has 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 the station you choose. At every 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(int[] gas, int[] cost) returns the index of the station to start from so that you complete one full circle, or -1 if no start works. When a start works, it is the only one.

    For gas = [1, 2, 3, 4, 5] and cost = [3, 4, 5, 1, 2] the answer is 3. Starting there, the tank holds 4, then 8 after the next station, then 7, 6 and 5, and the last stretch costs exactly 5. For gas = [2, 3, 4] and cost = [3, 4, 3] the answer is -1: the stations hold 9 units in all and the trip costs 10.

    Approach: skip every start that fails

    The brute-force way tries each station as the start and drives the whole circle, which is O(n²). The statement asks for O(n) time and O(1) extra space, so a start has to be ruled out without driving the circle again.

    The greedy solution has two parts.

    First, a feasibility check. 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. Otherwise a start exists.

    Second, find it in one drive. Drive once from station 0 to the last station, keeping the gas in the tank and a candidate start, first 0. When the tank drops below 0 on the stretch after station i, the car could not have driven it. Then no station from the candidate up to i can be the start either: from the candidate the car reached each of them with 0 or more gas, and starting at one of them means being there with an empty tank, which is never better. So the candidate moves to i + 1 and the tank restarts at 0.

    The last candidate works because of the first check. From it, the car reaches the end of the list without running dry. Each stretch it skipped loses gas overall, but the total isn't negative, so the gas left at the end covers everything those stretches lose, and the car gets around the circle back to its start.

    A trace

    For the first example, the second loop prints this after each station (tank is the gas on arriving at the next station, after any reset):

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

    At stations 0, 1 and 2 the tank goes negative (-2, -2 and -2, then resets), so the start moves to 3. From there it never drops again, and the function returns 3.

    Step by step

    1. Store the number of stations, gas.length, in n. The route is a circle, so after n - 1 comes 0.
    2. Create total, set to 0. It adds up what you take minus what you spend, over all stations.
    3. Loop over all stations and add gas[i] - cost[i] to total.
    4. If total < 0, return -1. Otherwise a start exists and the second loop finds it.
    5. Create start, set to 0. It is the station being tried. Station 0 goes first, and the start moves forward only when it fails.
    6. Create tank, set to 0. The car starts with an empty tank.
    7. Loop over the stations once more. One drive from station 0 to the last is enough.
    8. Add gas[i] - cost[i] to tank. Afterwards tank is the gas left on arriving at the next station.
    9. If tank < 0, the stretch couldn't be driven. Exactly 0 is fine: the car arrives empty and can still take the next station's gas.
    10. Move start to i + 1 and reset tank to 0, since the trip now begins again at the new start.
    11. After the loop, return start.

    The solution in Java

    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 Java details worth a closer look:

    • gas.length is a field. An array's size is read without parentheses, unlike String.length().
    • Two loops, two variables named i. The loop variable is scoped to its own for statement, so the second loop can reuse the name.
    • int is enough. With n up to 100,000 and values up to 10,000, the sums never pass 1,000,000,000, within the range of an int (about 2.1 billion). With n = 100000 stations of 10000 gas and 0 cost, the code still returns 0.
    • No wrap-around code. The second loop never goes past station n - 1. The first loop's check is what makes that safe: it proves the car has enough gas to cover the skipped stretches.

    Complexity

    Time: O(n). Two passes over the stations, with constant work at each.

    Space: O(1). Only the integers n, total, start and tank are kept.

    Edge cases and common mistakes

    • One station. gas = [5], cost = [4] returns 0. With gas = [4] and cost = [5] it returns -1.
    • A tank of exactly zero. gas = [3], cost = [3] returns 0. Testing tank <= 0 instead of tank < 0 would wrongly skip such a start.
    • All zeros. gas = [0, 0] and cost = [0, 0] returns 0: the total is 0, which isn't negative, and the tank never drops below 0.
    • Leaving out the total check. Without it, the second loop would return some candidate even when no start works. For gas = [4] and cost = [5] the loop alone would return 1, a station that doesn't exist.
    • Not resetting tank. The negative gas from the failed start would carry over and spoil the new candidate.
    • Moving start to i instead of i + 1. Station i is where the car just ran dry on the stretch after it, so it can't be the start.

    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 = [2, 0], cost = [1, 1] gives 0.
    • gas = [5], cost = [4] gives 0.
    • gas = [4], cost = [5] gives -1.
    Keep going with Java500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free