Start learning free

LESSON 458 OF 535 · GREEDY

Gas Station in C++: Greedy Single Pass in O(n)

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

    C++ · GREEDY

    Gas Station in C++, 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 a stretch can be driven only if the tank holds at least its cost.

    The function canCompleteCircuit returns the station to start from so that you get all the way around and back, 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 at station 3 the tank holds 4, then 8 at station 4, 7 at station 0, 6 at station 1 and 5 at station 2, which is exactly the cost of the last stretch back to station 3. For gas = [2, 3, 4] and cost = [3, 4, 3] the answer is -1: there are 9 units of gas for a trip that costs 10.

    Approach: two facts make one pass enough

    The direct way is to try every station as the start and simulate the whole circle, which costs O(n²). The statement asks for O(n) time and O(1) extra space, so the work has to be shared between starts. Two observations do that.

    First, a total check. Add up gas[i] - cost[i] over all stations. If the sum is negative, there is less gas in the whole circle than the trip costs, so no start can work: return -1. If it is not negative, some start exists, and the next pass finds it.

    Second, a failed start rules out everything it passed. Drive from a candidate start and keep the gas in tank. Whenever tank goes below 0 on the stretch after station i, the car could not have driven it. The candidate moves to i + 1 and the tank is emptied. Skipping every station from the old start to i is safe because the car reached each of them with 0 or more gas. Starting at one of them means being there with an empty tank, which is never better, so the tank would still go negative after station i.

    The last candidate is the answer because of the total check. The stretches skipped before it each lose gas overall, but the grand total is not negative, so what the tank holds at the end covers all of those losses. Inside a skipped stretch the tank only goes below 0 at the very end of it, so with that extra gas the car gets through each of them and back to the start.

    Here is the second pass on the first example, where the total check has already passed:

    i=0 tank=-2 -> start=1, tank reset
    i=1 tank=-2 -> start=2, tank reset
    i=2 tank=-2 -> start=3, tank reset
    i=3 tank=3
    i=4 tank=6
    start=3

    The third example, gas = [5, 1, 2, 3, 4] and cost = [4, 4, 1, 5, 1]:

    i=0 tank=1
    i=1 tank=-2 -> start=2, tank reset
    i=2 tank=1
    i=3 tank=-1 -> start=4, tank reset
    i=4 tank=3
    start=4

    Step by step

    1. Store the number of stations in an int named n, from gas.size().
    2. Create total, an int set to 0. It adds up what each station gives minus what the stretch after it costs.
    3. Loop over every station and add gas[i] - cost[i] to total. A station with less gas than its stretch costs adds a negative number.
    4. If total is negative, return -1. All the gas cannot pay for the trip, whatever the start.
    5. Create start, set to 0, the station being tried. Station 0 goes first, and the start moves only when it fails.
    6. Create tank, set to 0: the gas on board when driving from start.
    7. Loop over the stations once more from 0 to n - 1. One drive to the end of the list 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 is negative, the stretch could not be driven. Exactly 0 is fine: the car arrives empty and takes the next station's gas.
    10. Move start to i + 1, the key step, and set tank back to 0, because the trip now begins again at the new start.
    11. Return start after the loop.

    The solution in C++

    class Solution {
    public:
        int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
            int n = gas.size();
            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 C++ details worth a closer look:

    • The parameters are references. vector<int>& means the function reads the caller's vectors directly, with no copy. That keeps the extra space at O(1). The function never changes them.
    • int n = gas.size() converts the unsigned size to int. With n at most 100,000 that is safe, and it keeps the loop comparisons i < n between two int values.
    • int is enough for the sums. Each gas[i] - cost[i] is between -10,000 and 10,000, and a sum over 100,000 stations stays within 1,000,000,000, below the 32-bit limit of about 2.1 billion. Signed overflow would be undefined behaviour in C++, so it is worth checking that it cannot happen.
    • The return value is an index. It is 0-based, like the vectors, and -1 means no start exists.

    Complexity

    Time: O(n). There are two loops over the stations, each doing a constant amount of work per step.

    Space: O(1). The code keeps only a handful of integers: n, total, start, tank and the loop counter.

    Edge cases and common mistakes

    • A single station. gas = [5] with cost = [5] returns 0, since the tank holds exactly the cost. With cost = [6] the total is negative and the function returns -1.
    • Dropping the total check. Without it, the second loop still ends with some start. For gas = [2, 3, 4] and cost = [3, 4, 3] the loop alone would return 2 for an impossible trip, instead of -1.
    • Treating an empty tank as failure. The test is tank < 0, not tank <= 0. Arriving with exactly 0 is allowed, because the next station's gas is added before the next stretch is driven.
    • Forgetting to reset the tank. After moving start, the new trip must begin empty. Keeping the negative balance would blame the new start for the old one's shortfall.
    • Moving start to i instead of i + 1. Station i was reached with 0 or more gas and still failed, so starting there with an empty tank cannot do better. The next candidate is i + 1.
    • Stations that give nothing. With all zeros, gas = [0, 0] and cost = [0, 0], the tank is never negative and the function returns 0.

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