Skip to content

853. Car Fleet (Medium)

Problem

There are n cars driving to the same target. The i-th car is at position[i] with speed speed[i]. A faster car behind a slower one can never pass; it slows down to the slower car’s speed and they become a fleet. A fleet is a non-empty set of cars driving at the same position with the same speed. A car that catches up to a fleet at the exact target also counts as merging.

Return the number of fleets that arrive at target.

Example

  • target = 12, position = [10,8,0,5,3], speed = [2,4,1,1,3]3
  • target = 10, position = [3], speed = [3]1

LeetCode 853 · Link · Medium

Approach 1: Brute force, step-wise simulation

Simulate every time step, updating positions and collapsing cars that meet.

def car_fleet(target: int, position: list[int], speed: list[int]) -> int:
# Fragile, slow, and fiddly with floating point. Included for contrast.
cars = sorted(zip(position, speed))
# ... step positions, merge when adjacent positions equalize, etc.
# In practice this is buggy; don't do it.
raise NotImplementedError("Simulation is not recommended for this problem.")

Where the time goes, line by line

Variables: n = len(position), T = simulation time horizon.

LinePer-call costTimes executedContribution
sort + simulateO(n) per stepT stepsO(n · T) ← dominates

T is unbounded in general and may involve floating-point precision issues.

Complexity

  • Time: Hard to bound cleanly; effectively O(n · T) where T is the simulation horizon.
  • Space: O(n).

Don’t actually simulate, it’s imprecise and slow.

Approach 2: Sort by position descending; compute arrival times

Sort cars by their starting position, nearest-to-target first. Compute each car’s time to reach the target (assuming free travel). A new fleet forms whenever a car arrives strictly later than the fleet ahead of it; otherwise it merges.

def car_fleet(target: int, position: list[int], speed: list[int]) -> int:
cars = sorted(zip(position, speed), reverse=True) # L1: O(n log n) sort descending
fleets = 0 # L2: O(1)
last_arrival = 0.0 # L3: O(1) arrival of fleet ahead
for pos, spd in cars: # L4: n iterations
arrival = (target - pos) / spd # L5: O(1) time to target
if arrival > last_arrival: # L6: O(1) fleet check
fleets += 1 # L7: O(1) new fleet
last_arrival = arrival # L8: O(1) update
return fleets # L9: O(1)

Where the time goes, line by line

Variables: n = len(position).

LinePer-call costTimes executedContribution
L1 (sort)O(n log n)1O(n log n) ← dominates
L4-L8 (linear scan)O(1)nO(n)

The sort dominates; the scan is linear.

Complexity

  • Time: O(n log n), driven by L1 (sorting by position). Sorting dominates.
  • Space: O(n) for the sort.

Approach 3: Sort + monotonic stack of arrival times (equivalent, stack-explicit)

Same ordering, but use a stack of arrival times to make the “merge behind a slower car” more visual: push the car’s arrival time; pop it immediately if the stack top (car ahead) arrives later (or equal, since faster cars behind catch up and merge).

def car_fleet(target: int, position: list[int], speed: list[int]) -> int:
cars = sorted(zip(position, speed), reverse=True) # L1: O(n log n) sort
stack = [] # L2: O(1)
for pos, spd in cars: # L3: n iterations
t = (target - pos) / spd # L4: O(1) arrival time
if not stack or t > stack[-1]: # L5: O(1) check vs fleet ahead
stack.append(t) # L6: O(1) push new fleet
# otherwise: catches up to the fleet ahead, merged
return len(stack) # L7: O(1)

Where the time goes, line by line

Variables: n = len(position).

LinePer-call costTimes executedContribution
L1 (sort)O(n log n)1O(n log n) ← dominates
L3-L6 (scan + stack ops)O(1)nO(n)
L7 (len)O(1)1O(1)

Same cost structure as Approach 2; the stack just makes the fleet grouping explicit.

Complexity

  • Time: O(n log n), driven by L1 (sorting by position).
  • Space: O(n) for the stack.

Functionally equivalent to Approach 2; the stack makes the fleet structure explicit.

Summary

ApproachTimeSpace
Time-step simulationO(n · T)O(n)
Sort + arrival timesO(n log n)O(n)
Sort + monotonic stackO(n log n)O(n)

The insight: fleets are determined by arrival times in order of starting position, no need to simulate. A slower car ahead caps everyone behind it.

Test cases

# Quick smoke tests, paste into a REPL or save as test_car_fleet.py and run.
# Uses the canonical implementation (Approach 3: sort + monotonic stack).
def car_fleet(target: int, position: list[int], speed: list[int]) -> int:
cars = sorted(zip(position, speed), reverse=True)
stack = []
for pos, spd in cars:
t = (target - pos) / spd
if not stack or t > stack[-1]:
stack.append(t)
return len(stack)
def _run_tests():
assert car_fleet(12, [10,8,0,5,3], [2,4,1,1,3]) == 3
assert car_fleet(10, [3], [3]) == 1
assert car_fleet(100, [0,2,4], [4,2,1]) == 1 # all merge
assert car_fleet(10, [6,8], [3,2]) == 2 # car at 8 arrives faster, 2 fleets
print("all tests pass")
if __name__ == "__main__":
_run_tests()
  • Arrays, (position, speed) pairs; sort + pass
  • Stacks, visualization of fleet merges