title: Car Pooling slug: car-pooling difficulty: medium leetcode_id: 1094 leetcode_url: https://leetcode.com/problems/car-pooling/ categories: - arrays - sorting patterns: - prefix-sum - intervals description: | There is a car with `capacity` empty seats. The vehicle only drives east (i.e., it cannot turn around and drive west). You are given the integer `capacity` and an array `trips` where `trips[i] = [numPassengers_i, from_i, to_i]` indicates that the ith trip has `numPassengers_i` passengers and the locations to pick them up and drop them off are `from_i` and `to_i` respectively. The locations are given as the number of kilometers due east from the car's initial location. Return `true` *if it is possible to pick up and drop off all passengers for all the given trips*, or `false` *otherwise*. constraints: | - `1 <= trips.length <= 1000` - `trips[i].length == 3` - `1 <= numPassengers_i <= 100` - `0 <= from_i < to_i <= 1000` - `1 <= capacity <= 10^5` examples: - input: "trips = [[2,1,5],[3,3,7]], capacity = 4" output: "false" explanation: "At location 3, we pick up 3 passengers while still carrying 2 from the first trip. Total = 5 passengers, but capacity is only 4." - input: "trips = [[2,1,5],[3,3,7]], capacity = 5" output: "true" explanation: "At location 3, we have 2 + 3 = 5 passengers which exactly matches capacity. At location 5, the first group exits, leaving us with 3 passengers." explanation: intuition: | Imagine you're driving a shuttle bus along a straight highway, and passengers get on and off at various mile markers. At any given point, you need to make sure the number of passengers in the car never exceeds its capacity. The key insight is that we don't need to simulate the entire journey mile by mile. Instead, we only care about **what happens at specific locations** — the pickup and dropoff points. At each pickup location, passengers get on (increasing the count), and at each dropoff location, passengers get off (decreasing the count). Think of it like this: create a timeline of events along the route. Each event is either "+X passengers at location L" (pickup) or "-X passengers at location L" (dropoff). If we process these events in order of location and track the running total, we can check if we ever exceed capacity. Since the maximum location is constrained to 1000, we can use a simple **difference array** (or "sweep line" technique) where we mark passenger changes at each location, then compute a prefix sum to get the actual passenger count at each point. approach: | We solve this using a **Difference Array (Sweep Line) Approach**: **Step 1: Create a difference array** - Create an array of size 1001 (since `to_i <= 1000`) initialised to zeros - This array will track the *change* in passenger count at each location   **Step 2: Mark passenger changes** - For each trip `[numPassengers, from, to]`: - Add `numPassengers` at index `from` (passengers get on) - Subtract `numPassengers` at index `to` (passengers get off)   **Step 3: Compute running sum and check capacity** - Iterate through the difference array, maintaining a running sum of passengers - At each location, the running sum represents the current number of passengers in the car - If the running sum ever exceeds `capacity`, return `false`   **Step 4: Return result** - If we complete the iteration without exceeding capacity, return `true`   This works because the difference array captures all changes, and the prefix sum at any index gives us the total passengers at that location. common_pitfalls: - title: Simulating Every Mile description: | A naive approach might iterate through every location from 0 to max(to_i) and check passengers at each point. While this works, it's unnecessarily complex. The difference array approach is cleaner and more efficient — we process each trip once (O(n)) and then sweep through locations once (O(1001) = O(1) since it's bounded). wrong_approach: "Iterating through every location" correct_approach: "Use difference array with bounded sweep" - title: Forgetting Dropoff Happens Before Pickup description: | At a given location, passengers being dropped off exit **before** new passengers board. This is implicitly handled by the difference array approach since we're looking at the cumulative effect. However, if you're using an event-based approach with sorting, ensure dropoff events at the same location are processed before pickup events. Otherwise, you might incorrectly report a capacity violation. wrong_approach: "Processing pickups before dropoffs at same location" correct_approach: "Dropoffs before pickups, or use difference array" - title: Off-by-One with Dropoff Location description: | Passengers exit at the `to` location, meaning they are **not in the car** at that location. Make sure you subtract passengers at index `to`, not `to - 1`. For example, with trip `[2, 1, 5]`, passengers are in the car at locations 1, 2, 3, 4 but **not** at location 5. wrong_approach: "Subtracting at to - 1" correct_approach: "Subtract at exactly to" key_takeaways: - "**Difference array pattern**: When tracking cumulative changes over intervals, mark the change at start (+) and end (-), then compute prefix sums" - "**Bounded constraints enable simple solutions**: With `to_i <= 1000`, we can use a fixed-size array instead of a more complex data structure" - "**Sweep line technique**: This is a fundamental approach for interval problems — process events in order and maintain running state" - "**Similar problems**: Meeting Rooms II, My Calendar series, and other interval overlap problems use the same core idea" time_complexity: "O(n + k) where n is the number of trips and k is the range of locations (at most 1001). Since k is bounded by a constant, this is effectively O(n)." space_complexity: "O(k) where k is the range of locations (1001). This is O(1) in terms of input size since k is bounded by a constant." solutions: - approach_name: Difference Array (Sweep Line) is_optimal: true code: | def car_pooling(trips: list[list[int]], capacity: int) -> bool: # Difference array to track passenger changes at each location # Index represents location (0 to 1000) passenger_changes = [0] * 1001 # Mark pickup (+) and dropoff (-) for each trip for num_passengers, start, end in trips: passenger_changes[start] += num_passengers # Passengers get on passenger_changes[end] -= num_passengers # Passengers get off # Sweep through locations, tracking current passengers current_passengers = 0 for change in passenger_changes: current_passengers += change # Check if we ever exceed capacity if current_passengers > capacity: return False return True explanation: | **Time Complexity:** O(n + k) — Process each trip once, then sweep through k locations. With k bounded at 1001, this is effectively O(n). **Space Complexity:** O(k) — Fixed array of size 1001, which is O(1) in terms of input size. The difference array elegantly captures all passenger changes. The prefix sum at each index gives us the exact passenger count at that location, letting us verify capacity constraints in a single pass. - approach_name: Sort by Location with Events is_optimal: false code: | def car_pooling(trips: list[list[int]], capacity: int) -> bool: # Create events: (location, change in passengers) # Use negative change for dropoffs so they sort before pickups at same location events = [] for num_passengers, start, end in trips: events.append((start, num_passengers)) # Pickup (positive) events.append((end, -num_passengers)) # Dropoff (negative) # Sort by location, then by change (dropoffs before pickups at same spot) events.sort(key=lambda x: (x[0], x[1])) current_passengers = 0 for location, change in events: current_passengers += change if current_passengers > capacity: return False return True explanation: | **Time Complexity:** O(n log n) — Sorting the 2n events dominates. **Space Complexity:** O(n) — Storing 2n events. This approach explicitly creates events and sorts them. The key insight is that dropoffs (negative changes) naturally sort before pickups at the same location, correctly handling the constraint that passengers exit before new ones board. While slightly less efficient than the difference array approach, it's more intuitive and works for unbounded location ranges. - approach_name: Heap (Priority Queue) is_optimal: false code: | import heapq def car_pooling(trips: list[list[int]], capacity: int) -> bool: # Sort trips by start location trips.sort(key=lambda x: x[1]) # Min-heap of (end_location, num_passengers) for active trips active_trips = [] current_passengers = 0 for num_passengers, start, end in trips: # First, drop off passengers whose trips have ended while active_trips and active_trips[0][0] <= start: _, leaving = heapq.heappop(active_trips) current_passengers -= leaving # Pick up new passengers current_passengers += num_passengers if current_passengers > capacity: return False # Add this trip to active trips heapq.heappush(active_trips, (end, num_passengers)) return True explanation: | **Time Complexity:** O(n log n) — Sorting trips and heap operations. **Space Complexity:** O(n) — Heap can contain up to n trips. This approach processes trips in order of their start location. We use a min-heap to track when each group of passengers should exit, efficiently removing completed trips as we go. This is similar to the Meeting Rooms II problem and demonstrates the heap-based approach to interval scheduling.