Stage 1: Foundations·Stack & Queue Fundamentals
Medium#8536 min readStackSortingLeetCode

Car Fleet

Sort by position + fleet stack

Stack with a physical intuition — cars merge when a faster one catches a slower one.

Published Apr 15, 2026

Problem

You are given a target position, plus arrays position and speed for a set of cars on a one-lane road.

Cars move toward the target at constant speed. A car cannot pass another car, so if it catches up, it becomes part of the same fleet. Return the number of car fleets that arrive at the target.

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

Intuition

The only thing that matters is the order of cars by position, from closest to the target backward.

If you look at cars in that order, each car has an arrival time t = (target - position) / speed. A car forms a new fleet only if its arrival time is greater than the fleet ahead of it. Otherwise, it catches up and merges before the target.

That means the fleet times form a monotonic structure as you scan from front to back.

Approach

Sort by Position + Fleet Stack

TimeO(n log n)
SpaceO(n)
Recommended
  1. Pair each car with its position and speed.
  2. Sort the pairs by position descending.
  3. Walk from the car closest to the target to the farthest.
  4. Compute arrival time for each car.
  5. If the time is greater than the time of the fleet ahead, start a new fleet. Otherwise, merge into that fleet.

You can implement that with an explicit stack of fleet arrival times. In practice, only the top matters, but the stack makes the invariant concrete: fleet times are nondecreasing as you move away from the target.

impl Solution {
    pub fn car_fleet(target: i32, position: Vec<i32>, speed: Vec<i32>) -> i32 {
        let mut cars: Vec<(i32, i32)> = position.into_iter().zip(speed.into_iter()).collect();
        cars.sort_unstable_by(|a, b| b.0.cmp(&a.0));

        let mut fleets: Vec<f64> = Vec::with_capacity(cars.len());

        for (pos, spd) in cars {
            let time = (target - pos) as f64 / spd as f64;
            match fleets.last().copied() {
                None => fleets.push(time),
                Some(top) if time > top => fleets.push(time),
                _ => {}
            }
        }

        fleets.len() as i32
    }
}
Rust · runnable

Language notes:

  • Rustf64 is the simplest representation here because only comparisons matter. If you want to avoid floating point entirely, compare cross products instead.
  • TypeScripttime > fleets[last] is the only decision that matters. Equality means merge, because the rear car reaches the target at the same time as the fleet ahead.

Trace

For target = 12, position = [10, 8, 5, 3, 0], speed = [2, 4, 1, 3, 1] after sorting by position descending:

cararrival timefleet stack beforeactionfleet stack after
(10,2)1.0[]start new fleet[1.0]
(8,4)1.0[1.0]merge[1.0]
(5,1)7.0[1.0]start new fleet[1.0, 7.0]
(3,3)3.0[1.0, 7.0]merge[1.0, 7.0]
(0,1)12.0[1.0, 7.0]start new fleet[1.0, 7.0, 12.0]

The count is the number of times the arrival time strictly increases while scanning from the front.

Edge cases

  • Equal arrival times — they merge into one fleet.
  • Cars already sorted by position — still sort explicitly; the problem does not guarantee input order.
  • One car — always one fleet.
  • Fractional times — common and expected; do not round before comparing.
💡
Tip

This problem is a monotonic-stack problem in disguise. The stack stores fleet arrival times, and each new car either becomes a new top or disappears into the top fleet.

Takeaway

Sort by the dimension that defines interaction, then compress the rest into a monotonic stack or running frontier. Here, interaction happens only with the car or fleet directly ahead.

That is the core pattern: order first, then resolve locally.

More in Stack & Queue Fundamentals