The problem
A car with capacity empty seats drives in one direction. Each trip is [num, start, end]: pick up num passengers at location start and drop them at end. Return True if the car can complete every trip without ever carrying more than capacity people.
Input: trips = [[2, 1, 5], [3, 3, 7]], capacity = 4
Output: FalseBetween locations 3 and 5 both groups are in the car: 2 + 3 = 5 passengers, more than 4. With capacity = 5 the answer is True.
How to think about it
Start with brute force. For each location from the first pick-up to the last drop-off, add up the passengers of every trip covering it. With locations up to 1000 and trips up to 1000, that is about 10⁶ steps. It works, but it does not scale with the coordinate range.
The key insight: the load only changes at pick-ups and drop-offs. Record a change at each event instead of the load at each point: +num at start, -num at end. Then walk the event points in order and keep a running sum. That running sum is the load. This is a difference array, which is a prefix sum in reverse: you store the deltas and the prefix sum rebuilds the values.
Drop-offs happen before pick-ups at the same spot. Passengers leave at end, so a trip ending at 5 and one starting at 5 never share the car. Merging both into one delta[5] handles that automatically, because the net change is applied at once.
Which pattern is this? Intervals with a sweep line, the same idea as Meeting Rooms II but with weights. The tell is “intervals with a quantity, and a limit on the total at any moment”.
Approach
- Build
delta: for each trip addnumatstartand subtractnumatend. - Walk the locations in sorted order, adding
delta[t]to a runningcur. - If
curever exceedscapacity, returnFalse. - Otherwise return
True.
Solution
from collections import defaultdict
def car_pooling(trips, capacity):
delta = defaultdict(int)
for num, s, e in trips:
delta[s] += num; delta[e] -= num # board at s, leave at e
cur = 0
for t in sorted(delta): # sweep event points left to right
cur += delta[t] # running sum = passengers in the car
if cur > capacity: return False
return True
# car_pooling([[2,1,5],[3,3,7]], 4) -> False
# car_pooling([[2,1,5],[3,3,7]], 5) -> TrueDry run
delta = {1: +2, 5: -2, 3: +3, 7: -3}, swept in order 1, 3, 5, 7.
Location t |
delta[t] |
cur |
capacity 4 | capacity 5 |
|---|---|---|---|---|
| 1 | +2 | 2 | ok | ok |
| 3 | +3 | 5 | 5 > 4, return False | ok |
| 5 | -2 | 3 | — | ok |
| 7 | -3 | 0 | — | ok, return True |
Complexity
- Time: O(n log n). Up to
2nevent points are sorted; the sweep is linear. - Space: O(n). The
deltamap. (Because locations are at most 1000, a fixed array of size 1001 also works and gives O(n + 1000) time with no sort.)
Edge cases and pitfalls
- Drop-off and pick-up at the same location (
[[2, 1, 5], [3, 5, 7]]with capacity 3) returnsTrue: the -2 and +3 combine at location 5, so the load never goes above 3. - A single trip larger than capacity returns
Falseat its first event. - Processing pick-ups before drop-offs at the same point with separate events gives false
Falseanswers. Merging deltas per location, as here, avoids that. - Forgetting the import:
defaultdictcomes fromcollections.
Related problems
- Meeting Rooms II (LeetCode 253): the same sweep where every event has weight 1.
- Range Sum Query - Immutable (LeetCode 303): the prefix sum that rebuilds the load from the deltas.
- My Calendar I (LeetCode 729): capacity 1, with bookings arriving one at a time.
- 1.Meeting Rooms (LeetCode 252)
- 2.Merge Intervals (LeetCode 56)
- 3.Insert Interval (LeetCode 57)
- 4.Non-overlapping Intervals (LeetCode 435)
- 5.Meeting Rooms II (LeetCode 253)
- 6.Interval List Intersections (LeetCode 986)
- 7.Minimum Number of Arrows to Burst Balloons (LeetCode 452)
- 8.Car Pooling (LeetCode 1094)
- 9.My Calendar I (LeetCode 729)
- 10.Employee Free Time (LeetCode 759)