Skip to content

Commit e7a8f1f

Browse files
committed
solve Gas Station
1 parent eb8bb4b commit e7a8f1f

File tree

2 files changed

+133
-0
lines changed

2 files changed

+133
-0
lines changed

problems/gas_station.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
from typing import List
2+
3+
4+
class Solution:
5+
# Keep track of amount in tank.
6+
# Example:
7+
# 1, 2, 3, 4, 5
8+
# 3, 4, 5, 1, 2
9+
#
10+
# tank:
11+
# 0, -2 <- disqualified
12+
# 0, -2 <- disqualified
13+
# 0, -2 <- disqualified
14+
# 6, 4, 2, 0, 3 <- qualified
15+
#
16+
# For O(n) time disqualification, I propose the following conjecture:
17+
# Consider a list of integers, L.
18+
# Sum of integers in L is >= 0 <=> There exists a run in L which consist of
19+
# cumulative sums of corresponding integers in the run, each having value >= 0.
20+
#
21+
# To find the exact run, we need O(n^2) time.
22+
#
23+
# Time Complexity: O(n ^ 2).
24+
# Space Complexity: O(1) additional space.
25+
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
26+
if sum(gas) - sum(cost) < 0:
27+
# Rejection in O(n) time, O(1) space.
28+
return -1
29+
30+
n = len(gas)
31+
for i in range(n):
32+
cum_sum, j = 0, i
33+
while True:
34+
cum_sum += gas[j] - cost[j]
35+
if cum_sum < 0:
36+
break
37+
j = (j + 1) % n
38+
if j == i:
39+
return i
40+
41+
42+
class OfficialSolution:
43+
"""
44+
== Approach 1: One Pass. ==
45+
== Intuition ==
46+
The first idea is to check every single station.
47+
- Choose the station as starting point.
48+
- Perform the road trip and check how much gas we have in tank at each station.
49+
That means O(N ^ 2) time complexity, and for sure once could od better.
50+
51+
Let's notice two things.
52+
1. It's impossible to perform the road trip if sum(gas) < sum(cost). In this
53+
situation, the answer is -1.
54+
One could compute total amount of gas in the tank total_tank = sum(gas) -
55+
sum(cost) during the round trip, and then return -1 if total_tank < 0.
56+
2. It's impossible to start at a station i if gas[i] - cost[i] < 0, because then
57+
there is not enough gas in the tank to travel to i + 1 station.
58+
The second fact could be generalized. Let's introduce curr_tank variable to
59+
track the current amount of gas in the tank. If at some station curr_tank is
60+
less than 0, that means that one couldn't reach this station.
61+
Next step is to mark this station as a new starting point, and reset curr_tank to 0
62+
since one starts with no gas in tank.
63+
64+
== Algorithm ==
65+
Now the algorithm is straight forward.
66+
1. Initiate total_tank and curr_tank as 0, and choose station 0 as a starting
67+
station.
68+
2. Iterate over all stations:
69+
- Update total_tank and curr_tank at each step, by adding gas[i] and subtracting
70+
cost[i].
71+
- If curr_tank < 0 at i + 1 station, make i + 1 station a new starting point and
72+
rest curr_tank = 0 to start with an empty tank.
73+
3. Return -1 if total_tank < 0 and starting station otherwise.
74+
75+
== Complexity Analysis ==
76+
Time Complexity = O(N), since there is only 1 loop over all stations here.
77+
Space Complexity = O(1) since it's a constant space solution.
78+
79+
== Idea ==
80+
During scanning, whenever we find curr_tank is becoming negative, we reset curr_tank
81+
to 0, and consider the next index as our potential candidate for starting position.
82+
83+
This is a specific to a general problem solving heuristic:
84+
Whenever some variable of interest does not match the value who want it to contain,
85+
reset it and reset the potential candidate. This is useful for giving O(n) time
86+
algorithms.
87+
88+
Why does this work?
89+
Suppose total_tank >= 0.
90+
Consider the starting position given by the algorithm is Ns.
91+
For all gas stations to the right (till 0), our curr_tank is >= 0.
92+
This is maintained by the algorithm.
93+
What about the reverse run from 0 to Ns?
94+
Suppose curr_tank is negative at some k, between 0 and Ns.
95+
This means sum of deltas (gas-cost) is negative from Ns to 0 and then 0 to k.
96+
Also sum of deltas (gas-cost) is negative from k to Ns, otherwise the starting
97+
position would be before Ns.
98+
This means total_tank < 0, which is contradiction.
99+
"""
100+
101+
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
102+
total_tank = curr_tank = start_index = 0
103+
n = len(gas)
104+
for i in range(n):
105+
total_tank += gas[i] - cost[i]
106+
if curr_tank < 0:
107+
curr_tank = 0
108+
start_index = i
109+
curr_tank += gas[i] - cost[i]
110+
return -1 if total_tank < 0 else start_index

tests/test_gas_station.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import unittest
2+
3+
from gas_station import Solution, OfficialSolution
4+
5+
6+
class TestGasStation(unittest.TestCase):
7+
def test_example_1(self):
8+
assert (
9+
Solution().canCompleteCircuit(gas=[1, 2, 3, 4, 5], cost=[3, 4, 5, 1, 2])
10+
== 3
11+
)
12+
assert (
13+
OfficialSolution().canCompleteCircuit(
14+
gas=[1, 2, 3, 4, 5], cost=[3, 4, 5, 1, 2]
15+
)
16+
== 3
17+
)
18+
19+
def test_example_2(self):
20+
assert Solution().canCompleteCircuit(gas=[2, 3, 4], cost=[3, 4, 3]) == -1
21+
assert (
22+
OfficialSolution().canCompleteCircuit(gas=[2, 3, 4], cost=[3, 4, 3]) == -1
23+
)

0 commit comments

Comments
 (0)