Stacks & Queues · 30 of 38
Find the first circular tour that visits all Petrol Pumps
Find the first gas station on a round road where a car can start with an empty tank and drive all the way around without running dry.
- Simple way
- time O(n^2)
room O(1) - Clever way
- time O(n)
room O(1)
The problem, simply
Petrol pumps stand in a circle. Pump i gives petrol[i] litres, and it costs distance[i] litres to drive from pump i to the next pump. The truck starts empty, has an unlimited tank, and fills up fully at every pump it reaches.
Return the smallest index of a pump where the truck can start and drive the whole circle back to that pump. If no pump works, return -1.
A story to remember it
Picture it
Five pumps on a one way circle. The badge on each pump is the petrol it gives. The number on each arrow is the petrol needed to reach the next pump. Starting at P3 works.
- answer
Examples
A normal case
- In
petrol = [4, 6, 7, 4], distance = [6, 5, 3, 5]- Out
1
From pump 1 the tank goes 1, 5, 4, then 2 after the last stretch back to pump 1. Pump 0 fails at once: 4 petrol, 6 needed.
Not enough petrol in total
- In
petrol = [1, 2, 3], distance = [3, 4, 3]- Out
-1
There are 6 litres in total and the loop needs 10. No start can fix that.
Ends with an empty tank
- In
petrol = [1, 2, 3, 4, 5], distance = [3, 4, 5, 1, 2]- Out
3
From pump 3 the tank goes 3, 6, 4, 2, and then exactly 0 when it arrives back. Arriving with 0 still counts.
One pump
- In
petrol = [5], distance = [4]- Out
0
The loop is one stretch from the pump back to itself. 5 is enough for 4.
Way 1: the simple way
Try every pump as the start and drive around.
- For each
start, settank = 0. - Visit the
npumps fromstart, using(start + step) % n. Addpetrol, take awaydistance. - If
tankever drops below 0, this start fails. If allnstretches are done, returnstart.
Way 1 trying P0: the tank is 1 after the first stretch and -1 after the second. P0 fails. Then it tries P1 from scratch, and so on.
- looking now
- rejected
- path
class Solution {
public int tour(int[] petrol, int[] distance) {
int n = petrol.length;
for (int start = 0; start < n; start++) {
int tank = 0;
boolean ok = true;
for (int step = 0; step < n; step++) {
int i = (start + step) % n; // wrap around the circle
tank += petrol[i] - distance[i];
if (tank < 0) { ok = false; break; } // stuck on this stretch
}
if (ok) return start;
}
return -1;
}
}How long it takes: up to n starts, each driving up to n stretches, O(n^2). Extra room: O(1).
Way 2: the clever way
Two facts make one pass enough.
- Skip rule: if you start at
sand get stuck on the stretch after pumpi, then no pump fromstoican be the start. When you passed each of them, your tank was 0 or more, so starting there with an empty tank cannot do better. - Total rule: if total petrol is at least total distance, some start works. If it is less, none does.
Think of the pumps in the current try as a waiting line, a queue. Each step adds the next pump at the back. When the tank goes below 0, the whole line is thrown out, and the next pump starts a new line.
- Keep
start = 0,tank = 0,total = 0. - For each pump
i: addpetrol[i] - distance[i]to bothtankandtotal. - If
tank < 0, setstart = i + 1andtank = 0. - At the end, return
startiftotal >= 0, else-1.
One trip around, throwing out pumps that cannot be the start
01 / 08
Start the first try at P0 with an empty tank. The line of pumps in this try is empty.
- tank
- 0
- total
- 0
- looking now
- done
- answer
- rejected
- path
- just added
- ignored
class Solution {
public int tour(int[] petrol, int[] distance) {
int start = 0, tank = 0, total = 0;
for (int i = 0; i < petrol.length; i++) {
int gain = petrol[i] - distance[i];
tank += gain;
total += gain;
if (tank < 0) { // stuck: nobody from start to i can be the answer
start = i + 1;
tank = 0;
}
}
return total >= 0 ? start : -1;
}
}How long it takes: one pass over the pumps, O(n). Extra room: three numbers, O(1).
Watch out for
- The total check is required. Without it, the loop can end with
startpointing at a pump that does not work, or atn, which is not a pump at all. - Arriving with exactly 0 is fine. Fail only when the tank goes below 0.
- Petrol is gained at a pump before the next stretch is driven, so add
petrol[i] - distance[i]together in one step. - The well known queue version that keeps
frontandrearpointers fails for one pump ifrearstarts at 1 without% n. Testn = 1.
What interviewers ask next
- Why does the total rule hold? Draw the running sum of
petrol - distance. Start right after the place where it is lowest. From there, the sum never dips below where it began, so the tank never goes below 0. - Is the answer unique? Not always, for example
petrol = [2, 2],distance = [1, 1]works from both pumps. This method returns the smallest valid index, because every index before it was ruled out. - What if the tank has a limit
C? Then you cannot store everything, and the simple trick breaks. You need to simulate with the cap, or use more careful math. - What if the truck can go both ways? Run the same pass on the reversed circle and take whichever works.
- This is LeetCode 134, Gas Station.
Remember this
Grown-up words: the friends with lemonade are petrol pumps, the one pass with restarts is a greedy algorithm, the running tank is a prefix sum, the pumps in the current try form a sliding queue, and the skip rule is the key lemma that makes it O(n).