Leetcode 134. Gas Station

Problem Explanation:

There are N number of gas stations along a circular route. We have information about the gas present in each station and the cost of gas required to travel to the next station. With the provided data, the car has to start from one of the stations with an empty gas tank and has to complete a circuit once in a clockwise direction. If the journey around the circuit can be made, the index of the starting station has to be returned. If the journey around the circuit cannot be completed, -1 has to be returned.

Walk Through an Example:

As per the first given example, we have the following data: gas = [1,2,3,4,5] and cost = [3,4,5,1,2]

Starting from station 0 and moving in the clockwise direction, the tank runs out of gas at station 1 and the car cannot move to the next station. So, we start from the next station, i.e., station 1 and the same issue is encountered; the car runs out of gas at station 2. On further trying with station 2 and station 3, the car still fails to complete the circuit. On starting from station 3, the car successfully completes the circuit. Hence, the index of the starting station is 3.

Approach Explanation:

The solution to this problem involves finding the total gas available and the total gas required to travel to all the stations. The difference between the total gas and the total cost gives us the net gas available for travel. If the net available gas is lesser than 0, it indicates that we do not have enough gas to travel to all the stations hence -1 is returned.

If we have enough net gas, we try to start the journey from each station and continue to the next station if possible otherwise move on to the next station and try to start from there. If the car can successfully travel from a station and complete the circuit, the index of starting station is returned.

The solution to this problem makes use of greedy algorithm approach, where we make the locally optimal choice at each stage with the hope of finding a global optimum.

Steps:

  1. Find the total gas available and the total gas required;
  2. If the total available gas is less than the total gas required, return -1; else,
  3. Try starting the journey from all stations: If the car can move to the next station, subtract the gas cost from the total gas and add the gas from next station to the total gas. If the car cannot move to the next station, reset the total gas and cost to 0 and try to start from the next station.
  4. Once the car successfully completes the circuit, return the starting station index.

The solution has a time complexity of O(n) and a space complexity of O(1).

C++ Solution:

1
2cpp
3#include <vector>
4#include <numeric>
5
6using namespace std;
7
8class Solution {
9public:
10    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
11      // find total gas available and total cost
12        const int total_gas = accumulate(gas.begin(), gas.end(), 0);
13        const int total_cost = accumulate(cost.begin(), cost.end(), 0);
14      
15      // if total gas is less than cost, cannot travel hence return -1
16        if (total_gas < total_cost)
17            return -1;
18
19        int sum = 0;
20        int start = 0;
21
22      // try starting from each station
23        for (int i = 0; i < gas.size(); ++i) {
24          
25            sum += gas[i] - cost[i];
26            
27          // if cannot travel to next station with available gas
28            if (sum < 0) {
29                sum = 0;
30                start = i + 1;  // Try to start from next index
31            }
32        }
33        return start; // starting index of successful journey
34    }
35};

Python Solution:

1
2py
3class Solution:
4    def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
5        if sum(gas) < sum(cost):
6            return -1
7        n, start, accumulate = len(gas), 0, 0
8        for i in range(n):
9            if gas[i] + accumulate < cost[i]:
10                start, accumulate = i + 1, 0
11            else:
12                accumulate += gas[i] - cost[i]
13        return start

Java Solution:

1
2java
3class Solution {
4    public int canCompleteCircuit(int[] gas, int[] cost) {
5        int totalTank = 0;
6        int curTank = 0;
7        int startingStation = 0;
8        for (int i = 0; i < gas.length; ++i) {
9            totalTank += gas[i] - cost[i];
10            curTank += gas[i] - cost[i];
11            if (curTank < 0) {
12                startingStation = i + 1;
13                curTank = 0;
14            }
15        }
16        return (totalTank >= 0) ? startingStation : -1;
17    }
18}

JavaScript Solution:

1
2js
3var canCompleteCircuit = function(gas, cost) {
4    let start = 0;
5    let sumGas = 0;
6    let sumCost = 0;
7    let tank = 0;
8  
9    for(let i = 0; i < gas.length; i++){
10        sumGas += gas[i];
11        sumCost += cost[i];
12        tank += gas[i] - cost[i];
13        if(tank < 0){
14            start = i + 1;
15            tank = 0;
16        }
17    }
18    if(sumGas < sumCost){
19        return -1;
20    } else {
21        return start;
22    }
23};

C# Solution:

1
2csharp
3public class Solution {
4    public int CanCompleteCircuit(int[] gas, int[] cost) {
5        int totalGas = 0;
6        int currentGas = 0;
7        int startStation = 0;
8        for(int i = 0; i < gas.Length; ++i)
9        {
10            totalGas += gas[i] - cost[i];
11            currentGas += gas[i] - cost[i];
12            if(currentGas < 0)
13            {
14                startStation = i + 1;
15                currentGas = 0;
16            }
17        }
18        return totalGas >= 0 ? startStation : -1;
19    }
20}

All the above coding solutions pick a potential starting point, check gas reserves at each point, and change the starting point as needed. Even though the solution iterates the gas and cost arrays twice, it ensures the solution’s correctness by checking all possible combinations. The coding solution guarantees that if a solution exists, it must be unique.Explanation of Differences in Each Language:

If you look at the C++ version, you'll notice it uses the accumulate() function from the <numeric> library to find the total gas and total cost. The Python version achieves this by using the sum() function. Java does this through a for loop. JavaScript and C# also use a similar looping construct to sum up the gas and costs.

Python's List[int] is equivalent to Java's int[], C# int[], JavaScript's Array and C++ vector<int>.

Moving on to control flow, most languages share a similar syntax. For example if-else syntax is common across the languages. The for loop construct is also common, though the syntax may vary.

While C++ and C# both use the ++i syntax to increment a counter in a loop, Python, JavaScript, and Java use the similar i++ syntax. The difference is that ++i increments i, and then returns i, while i++ returns i, and then increments i.

Overall, the logic in every solution is identical, only the language-specific syntax and functions/methods vary.

It's important to note that these solutions will handle large inputs efficiently due to their linear time complexity, O(n). Even for large inputs with thousands or millions of gas stations, these solutions will complete in a reasonable amount of time. Furthermore, because they only use a fixed amount of space, these solutions have a constant space complexity of O(1), meaning they will not consume more memory as the size of the input increases.

This problem demonstrates how an algorithm implemented with a greedy approach can offer an efficient solution for problems related to system optimization (like finding the best route, or the maximum/minimum cost), even when the input size increases significantly. Despite some syntax differences, the underlying logic remains the same across all languages delivered – an optimal greedy choice at each step results in a globally optimal solution.


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫