Start learning free

LESSON 466 OF 543 · GREEDY

Gas Station in Rust: O(n) Greedy Single 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

    RUST · GREEDY

    Gas Station in Rust, 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 the route continues at station 0. The tank has no size limit and starts empty. At each station you take all its gas and then drive on. A stretch can be driven only if the tank holds at least its cost.

    The function can_complete_circuit 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.

    In the first example, gas = [1, 2, 3, 4, 5] and cost = [3, 4, 5, 1, 2]. Starting at station 3 gives a tank of 4, then 8 after station 4, then 7, 6 and 5 at stations 0, 1 and 2, and the last stretch back to station 3 costs exactly 5. The answer is 3.

    Approach: skip the stations that can't be the start

    The brute force tries every start and simulates the whole circle, which costs O(n²). The lesson does it in one pass with two observations.

    The total decides whether a start exists. Add up gas[i] - cost[i] over all stations. If the total is negative, the trip around costs more than all the gas available, and no start works. The second example is like that: the stations hold 2 + 3 + 4 = 9 units and the circle costs 3 + 4 + 3 = 10. If the total is not negative, a start exists.

    A failure rules out a whole stretch. Drive from a candidate start, keeping the tank. If the tank drops below 0 after station i, the car couldn't have driven that stretch from start. Now consider any station between start and i. Starting at the candidate, 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 by i. None of those stations can be the start, and the next candidate is i + 1 with an empty tank.

    When the pass ends, the last candidate is the answer. From it the car reaches the end of the list without running dry. Each stretch skipped before it lost gas overall, but the grand total isn't negative, so the gas left at the end covers everything the skipped stretches lost. Inside a skipped stretch the tank only went negative at its very end, so with that spare gas the car also gets through each of them and back to the start.

    Here is the pass for 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=0
    i=2  tank=1
    i=3  tank=-1  -> start=4, tank=0
    i=4  tank=3
    

    The pass ends with start = 4, which is the answer.

    Step by step

    1. Store the number of stations in n with gas.len(). The route is a circle, but the code never wraps around: one pass over the list is enough.
    2. Create total as a mutable variable at 0. It sums what each station gives minus what the stretch after it costs.
    3. Loop over the stations with i over 0..n.
    4. Add gas[i] - cost[i] to total with +=. It is negative for a station that gives less than the next stretch costs.
    5. Return -1 when total is negative. Otherwise a start exists and the second loop finds it.
    6. Create start, mutable, at 0. Station 0 is tried first, and the candidate moves only when it fails.
    7. Create tank, mutable, at 0: the gas in the tank when driving from start.
    8. Loop over the stations again with i over 0..n. A single drive from station 0 to the last station is enough.
    9. Add gas[i] - cost[i] to tank. Afterwards, tank is the gas left on arriving at the next station.
    10. Check whether tank is negative. Exactly 0 is fine: the car arrives empty and can still take the next station's gas.
    11. Move start to i + 1. This is the key step: no station from the old start to i can be the start.
    12. Reset tank to 0, because the trip now begins again at the new start.
    13. Return start as i32. The index is a usize and the function returns an i32.

    The solution in Rust

    impl Solution {
        fn can_complete_circuit(gas: Vec<i32>, cost: Vec<i32>) -> i32 {
            let n = gas.len();
            let mut total = 0;
            for i in 0..n {
                total += gas[i] - cost[i];
            }
            if total < 0 {
                return -1;
            }
            let mut start = 0;
            let mut tank = 0;
            for i in 0..n {
                tank += gas[i] - cost[i];
                if tank < 0 {
                    start = i + 1;
                    tank = 0;
                }
            }
            start as i32
        }
    }
    

    The Rust details worth a closer look:

    • Inferred types. total and tank start as the literal 0 and become i32 because they are added to gas[i] - cost[i], which is an i32. The values are at most 10,000 each and there are at most 100,000 stations, so the sums stay far from the i32 limits.
    • start is a usize. Assigning i + 1 to it makes the compiler infer usize, which is why the function ends with start as i32. The cast is a plain conversion here: the value is at most n, far below i32::MAX.
    • start can become n. If the last stretch fails, start is set to n, one past the last station. That can't be returned, because the total check already guaranteed that the stretches can't all fail at the end: when the total is not negative, the last candidate is a real station.
    • Early return. return -1; leaves the function at once. The final line, start as i32, has no semicolon and is the value returned otherwise.
    • Indexing. gas[i] panics when i is out of bounds, but both loops stop at n, and the lesson's inputs have equal lengths for gas and cost.

    Complexity

    Time: O(n). There are two simple passes over the stations, and each step does a few additions and one comparison.

    Space: O(1). The code keeps four variables, n, total, start and tank, however long the input is.

    Edge cases and common mistakes

    • A single station. gas = [5] and cost = [4] returns 0, and gas = [4] with cost = [5] returns -1, because the total is -1.
    • A tank of exactly zero. The check is tank < 0. Using tank <= 0 would reject a start that arrives at the next station with exactly 0 and could continue. For gas = [3, 1, 1] and cost = [1, 2, 2] the tank is 2, 1, 0 and the answer is 0; with <= the last step would move start past the end.
    • Skipping the total check. Without it, the second loop still ends with some start, so the second example would return 2 instead of -1. The total check is what makes the last candidate trustworthy.
    • Restarting at i instead of i + 1. Station i is where the tank ran dry on the stretch after it, so it can't be the start either.
    • Forgetting to reset the tank. A negative tank carried into the new trip would make every later station look worse than it is.
    • Everything zero. With gas = [0, 0] and cost = [0, 0] every start works, which the lesson excludes, but the code 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 = [4] gives 0.
    • gas = [4], cost = [5] gives -1.
    • gas = [3, 1, 1], cost = [1, 2, 2] gives 0.
    Keep going with Rust500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free