Quick Overview

Given every booking for a bike rental service as a start and end time, compute the peak number of bikes in use at the same moment, which is the smallest fleet that serves all bookings. Tests interval-overlap reasoning, half-open boundary handling and an efficient method for up to 100,000 unsorted bookings.

Peak number of bikes needed to cover all pre-booked rental intervals

Company: Faire

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Onsite

A bike rental service receives every booking ahead of time. Each booking reserves one bike from a start time until an end time. Write a function that returns the peak number of bikes needed at any point in time: the number of bookings in use at the busiest moment, which is also the smallest fleet that can serve every booking. ### Function Signature ```python def peak_bike_demand(bookings: list[list[int]]) -> int: ``` ### Rules - `bookings[i] = [start, end]` reserves one bike for the half-open interval `[start, end)`: the bike is in use at every time `t` with `start <= t < end`. - A bike returned at time `t` can be rented out again at time `t`, so a booking that ends at `t` and a booking that starts at `t` never need two bikes at once. - Bikes are interchangeable. Identical bookings are separate bookings, and each needs its own bike. - Return the maximum, over all times `t`, of the number of bookings in use at `t`. Return `0` when there are no bookings. ### Constraints - `0 <= len(bookings) <= 10^5` - `0 <= start < end <= 10^9` for every booking - Times are integers (for example, minutes from the start of the booking period), so every value fits in a 32-bit signed integer. - `bookings` is in no particular order. ### Examples **Example 1** ```text Input: bookings = [[1, 4], [2, 5], [7, 9], [3, 6]] Output: 3 ``` At time `3`, the bookings `[1, 4)`, `[2, 5)` and `[3, 6)` are all in use. No moment has more. **Example 2** ```text Input: bookings = [[1, 3], [3, 5], [5, 7]] Output: 1 ``` Each booking starts exactly when the previous one ends, so one bike can serve all three in turn. **Example 3** ```text Input: bookings = [[5, 10], [5, 10], [9, 12], [10, 11]] Output: 3 ``` At time `9`, both copies of `[5, 10)` and the booking `[9, 12)` are in use. At time `10`, the two copies have ended, leaving `[9, 12)` and `[10, 11)`.

Overview: Given every booking for a bike rental service as a start and end time, compute the peak number of bikes in use at the same moment, which is the smallest fleet that serves all bookings. Tests interval-overlap reasoning, half-open boundary handling and an efficient method for up to 100,000 unsorted bookings.

Read the full Faire Software Engineer interview experience this question came from

A bike rental service receives every booking in advance. Each booking reserves one bike from a start time until an end time. Given all bookings, return the peak number of bikes needed at any moment: the number of bookings in use at the busiest time, which is also the smallest fleet that can serve every booking. Implement `peak_bike_demand(bookings)`. ### Rules - `bookings[i] = [start, end]` reserves one bike for the half-open interval `[start, end)`: the bike is in use at every time `t` with `start <= t < end`. - A bike returned at time `t` can be rented out again at time `t`, so a booking that ends at `t` and a booking that starts at `t` never need two bikes at once. - Bikes are interchangeable. Identical bookings are separate bookings, and each needs its own bike. - Return the maximum, over all times `t`, of the number of bookings in use at `t`. Return `0` when there are no bookings. ### Constraints - `0 <= len(bookings) <= 10^5` - `0 <= start < end <= 10^9` for every booking - Times are integers, so every value fits in a 32-bit signed integer (no value exceeds 2^31-1); the answer is at most `len(bookings)`. - `bookings` is in no particular order. ### Example 1 ```text Input: bookings = [[1, 4], [2, 5], [7, 9], [3, 6]] Output: 3 ``` At time `3`, the bookings `[1, 4)`, `[2, 5)` and `[3, 6)` are all in use. No moment has more. ### Example 2 ```text Input: bookings = [[5, 10], [5, 10], [9, 12], [10, 11]] Output: 3 ``` At time `9`, both copies of `[5, 10)` and the booking `[9, 12)` are in use. At time `10`, the two copies have ended, leaving `[9, 12)` and `[10, 11)`.

Constraints

  • 0 <= len(bookings) <= 10^5
  • 0 <= start < end <= 10^9 for every booking
  • Times are integers; every value fits in a 32-bit signed integer
  • bookings is in no particular order

Examples

Input: ([],)

Expected Output: 0

Explanation: No bookings, so no bike is ever needed (empty minimum).

Input: ([[4, 9]],)

Expected Output: 1

Explanation: A single booking needs exactly one bike.

Hints

  1. The number of bikes in use can only change at a booking's start time or end time.
  2. Decide carefully what happens at a time where one booking ends and another begins: the returned bike is already available to the new booking.
  3. Times reach 10^9, so anything sized by the time range is too large; work with the bookings' own times instead.

Loading coding console...

Show the approach

Approach

Only a start can raise the number of bikes in use, so the peak is reached right after some booking starts. Sort all start times and, separately, all end times. Walk the starts in increasing order with a pointer into the sorted ends. Before counting a start s, advance the end pointer past every end <= s and decrement the running count for each one: bookings are half-open, so a bike returned at s is free before a booking starting at s needs one. Then increment the count for s and update the maximum. Invariant: after the last start equal to s is processed, the count equals (bookings with start <= s) minus (bookings with end <= s), which is exactly the number of bookings in use at time s. It never goes negative, because every end <= s belongs to a booking whose start < s was already counted. Taking the maximum over all start times therefore gives the maximum over all times. Edge cases: an empty list skips the loop and returns 0; identical bookings contribute separate starts and ends, so each is counted; touching bookings (one ends where another starts) are never counted together; times up to 10^9 are only compared, never used as indices, so no time-indexed array is needed and every value and the answer fit in a 32-bit int.

Time complexity:
O(n log n)
Space complexity:
O(n)