A clear explanation of counting car fleets by sorting cars by position and tracking arrival times.
Problem Restatement
There are n cars driving toward the same destination.
The destination is at position target.
For each car:
| Data | Meaning |
|---|---|
position[i] | Starting position of car i |
speed[i] | Speed of car i |
A car cannot pass another car ahead of it.
If a faster car catches up to a slower car before the destination, they become one fleet. The faster car slows down and follows the slower car.
A single car also counts as one fleet.
If a car catches a fleet exactly at the destination, it still counts as part of that fleet.
Return the number of car fleets that arrive at the destination.
Input and Output
| Item | Meaning |
|---|---|
| Input | target, the destination position |
| Input | position, the starting positions of the cars |
| Input | speed, the speeds of the cars |
| Output | The number of car fleets that arrive at target |
Function shape:
class Solution:
def carFleet(self, target: int, position: list[int], speed: list[int]) -> int:
...Examples
Example 1:
target = 12
position = [10, 8, 0, 5, 3]
speed = [2, 4, 1, 1, 3]Compute how long each car needs to reach the target:
| Position | Speed | Time to target |
|---|---|---|
10 | 2 | (12 - 10) / 2 = 1 |
8 | 4 | (12 - 8) / 4 = 1 |
0 | 1 | (12 - 0) / 1 = 12 |
5 | 1 | (12 - 5) / 1 = 7 |
3 | 3 | (12 - 3) / 3 = 3 |
Sort cars from closest to the target to farthest from the target:
| Position | Time |
|---|---|
10 | 1 |
8 | 1 |
5 | 7 |
3 | 3 |
0 | 12 |
The car at position 10 forms a fleet with arrival time 1.
The car at position 8 also has time 1, so it catches the first car exactly at the target. They are one fleet.
The car at position 5 takes time 7, so it cannot catch the fleet ahead. It forms a new fleet.
The car at position 3 takes time 3, so it catches the fleet ahead whose arrival time is 7. It joins that fleet.
The car at position 0 takes time 12, so it cannot catch the fleet ahead. It forms a new fleet.
So the answer is:
3Example 2:
target = 10
position = [3]
speed = [3]There is only one car, so there is one fleet.
1Example 3:
target = 100
position = [0, 2, 4]
speed = [4, 2, 1]Times to target:
| Position | Speed | Time |
|---|---|---|
4 | 1 | 96 |
2 | 2 | 49 |
0 | 4 | 25 |
The cars behind are faster and will catch the slower car ahead. They all become one fleet.
1First Thought: Simulate the Cars
One direct idea is to simulate the movement of every car over time.
At each moment, we could update every car’s position and merge cars when they meet.
This is not a good approach.
The cars move continuously, not in fixed integer steps. Choosing a time step can make the simulation inaccurate. A very small time step would also be too slow.
We need a mathematical way to know whether cars merge.
Key Insight
The important value for each car is its time to reach the target if it drives alone:
time = (target - position) / speedCars closer to the target affect cars behind them.
So we sort cars by position from closest to the target to farthest from the target.
Then we process cars in that order.
When we look at a car behind a fleet:
- If its arrival time is less than or equal to the fleet ahead, it catches that fleet.
- If its arrival time is greater, it cannot catch the fleet ahead, so it forms a new fleet.
Why?
A car behind can only catch a car or fleet ahead if it would arrive no later than that car or fleet when driving alone.
If it would arrive later, it is too slow to catch up before the target.
Algorithm
Pair every car’s position with its speed.
cars = list(zip(position, speed))Sort cars by position in descending order.
cars.sort(reverse=True)This lets us process cars from closest to the target to farthest.
Maintain:
fleets = 0
slowest_time_ahead = 0For each car:
- Compute its time to target.
- If this time is greater than
slowest_time_ahead, it cannot catch the fleet ahead. - Count it as a new fleet.
- Update
slowest_time_ahead.
If the time is less than or equal to slowest_time_ahead, the car joins the fleet ahead, so we do not increase the count.
Correctness
After sorting by position descending, every car we process is behind all cars already processed.
The variable slowest_time_ahead stores the arrival time of the fleet directly relevant to the current car. It is the time the current car must beat or match to catch the fleet ahead.
If the current car’s time is less than or equal to slowest_time_ahead, then it would reach the destination no later than the fleet ahead if it drove alone. Since it starts behind that fleet, it must catch the fleet before or at the target. Therefore, it belongs to the same fleet.
If the current car’s time is greater than slowest_time_ahead, then even driving alone it would arrive later than the fleet ahead. It cannot catch that fleet before the target. Therefore, it forms a new fleet.
The algorithm counts exactly one fleet each time a car cannot catch the fleet ahead. Every other car merges into an existing fleet. Therefore, the returned count is correct.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(n log n) | Sorting the cars dominates the runtime |
| Space | O(n) | We store the paired cars |
The scan after sorting takes O(n) time.
Implementation
class Solution:
def carFleet(self, target: int, position: list[int], speed: list[int]) -> int:
cars = sorted(zip(position, speed), reverse=True)
fleets = 0
slowest_time_ahead = 0
for pos, spd in cars:
time = (target - pos) / spd
if time > slowest_time_ahead:
fleets += 1
slowest_time_ahead = time
return fleetsCode Explanation
We first pair positions and speeds:
cars = sorted(zip(position, speed), reverse=True)Sorting in reverse order puts cars closer to the target first.
For example:
position = [10, 8, 0, 5, 3]becomes:
[10, 8, 5, 3, 0]Then we keep two values:
fleets = 0
slowest_time_ahead = 0fleets counts how many separate fleets we have found.
slowest_time_ahead stores the arrival time of the last fleet ahead.
For every car:
time = (target - pos) / spdIf this car needs more time than the fleet ahead:
if time > slowest_time_ahead:then it cannot catch that fleet. So it becomes a new fleet:
fleets += 1
slowest_time_ahead = timeIf time <= slowest_time_ahead, the car catches the fleet ahead, so we do nothing.
This also handles the case where a car catches a fleet exactly at the destination. Equal time means same fleet.
Testing
def test_car_fleet():
s = Solution()
assert s.carFleet(
12,
[10, 8, 0, 5, 3],
[2, 4, 1, 1, 3],
) == 3
assert s.carFleet(
10,
[3],
[3],
) == 1
assert s.carFleet(
100,
[0, 2, 4],
[4, 2, 1],
) == 1
assert s.carFleet(
10,
[1, 4],
[3, 2],
) == 1
assert s.carFleet(
10,
[4, 1, 0, 7],
[2, 2, 1, 1],
) == 3
print("all tests passed")
test_car_fleet()Test meaning:
| Test | Why |
|---|---|
| Standard multi-car case | Checks multiple merges and separate fleets |
| One car | Minimum non-empty input |
| All cars merge | Checks faster cars behind slower cars |
| Catch exactly at destination | Equal arrival time should count as one fleet |
| Mixed case | Checks both merge and non-merge behavior |