LeetCode 2251 - Number of Flowers in Full Bloom
The problem asks us to determine, for a list of people arriving at certain times, how many flowers are in full bloom at the time of their arrival. Each flower has a start and end time representing the period it is in full bloom, inclusive of both endpoints.
Difficulty: 🔴 Hard
Topics: Array, Hash Table, Binary Search, Sorting, Prefix Sum, Ordered Set
Solution
Problem Understanding
The problem asks us to determine, for a list of people arriving at certain times, how many flowers are in full bloom at the time of their arrival. Each flower has a start and end time representing the period it is in full bloom, inclusive of both endpoints. The input consists of a 2D array flowers, where flowers[i] = [starti, endi], and an array people indicating the arrival times of each person. The output is an array answer, where answer[i] is the number of flowers in bloom when people[i] arrives.
Constraints indicate that there can be up to 50,000 flowers and 50,000 people, and the times can go up to 10^9. This tells us that a naive approach checking every flower for each person will be too slow, as it would require up to 2.5 * 10^9 comparisons in the worst case. Edge cases include multiple flowers starting and ending at the same time, people arriving before any flower blooms, and people arriving exactly at a flower's start or end time.
The problem guarantees that start and end times are valid (starti <= endi) and that both arrays are non-empty.
Approaches
The brute-force approach is straightforward: for each person, iterate over all flowers and check if the person's arrival time is within the flower's bloom interval. While this works correctly, it has a time complexity of O(n * m), where n is the number of people and m is the number of flowers. Given the constraints, this approach is too slow.
The optimal approach relies on the observation that if we separate flower start times and end times into two sorted arrays, we can determine the number of flowers in bloom at any time t by counting the number of flowers that have started by t and subtracting the number that have already ended before t. This reduces the problem to sorting and using binary search, which is efficient for large inputs.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n * m) | O(1) | Check each flower for each person |
| Optimal | O(n log n + m log m) | O(n + m) | Sort start and end times, use binary search per person |
Algorithm Walkthrough
-
Extract all start times and all end times from
flowersinto separate arraysstartsandends. -
Sort both
startsandendsarrays in ascending order. This allows us to perform efficient binary searches later. -
Initialize an empty array
answerto store the results for each person. -
For each person's arrival time
pinpeople: -
Use binary search (
bisect_right) to find the number of flowers whose start time is less than or equal top. This gives the count of flowers that have started blooming byp. -
Use binary search (
bisect_right) to find the number of flowers whose end time is less thanp. This gives the count of flowers that have already ended beforep. -
Subtract the second count from the first count to get the number of flowers in bloom at time
p. -
Append this value to
answer. -
Return the
answerarray.
Why it works: The invariant here is that at any time p, the number of flowers in bloom equals the number of flowers that have started by p minus those that have already ended before p. Sorting allows us to count these efficiently with binary search.
Python Solution
from bisect import bisect_right
from typing import List
class Solution:
def fullBloomFlowers(self, flowers: List[List[int]], people: List[int]) -> List[int]:
starts = sorted(f[0] for f in flowers)
ends = sorted(f[1] for f in flowers)
answer = []
for p in people:
started = bisect_right(starts, p)
ended = bisect_right(ends, p - 1)
answer.append(started - ended)
return answer
The solution first sorts the start and end times. For each person's arrival, bisect_right is used to find the number of flowers that have started blooming by that time and the number that have ended before that time. The difference is appended to the result array.
Go Solution
import (
"sort"
)
func fullBloomFlowers(flowers [][]int, people []int) []int {
n := len(flowers)
starts := make([]int, n)
ends := make([]int, n)
for i, f := range flowers {
starts[i] = f[0]
ends[i] = f[1]
}
sort.Ints(starts)
sort.Ints(ends)
answer := make([]int, len(people))
for i, p := range people {
started := sort.SearchInts(starts, p+1)
ended := sort.SearchInts(ends, p)
answer[i] = started - ended
}
return answer
}
In Go, sort.SearchInts is used instead of bisect_right. SearchInts(arr, x) returns the index where x would be inserted to keep arr sorted, which corresponds to the number of elements ≤ x-1 depending on how we adjust x. We handle the same calculation logic as Python.
Worked Examples
Example 1: flowers = [[1,6],[3,7],[9,12],[4,13]], people = [2,3,7,11]
| Person | Flowers Started | Flowers Ended | In Bloom |
|---|---|---|---|
| 2 | 1 | 0 | 1 |
| 3 | 2 | 0 | 2 |
| 7 | 3 | 1 | 2 |
| 11 | 4 | 2 | 2 |
Example 2: flowers = [[1,10],[3,3]], people = [3,3,2]
| Person | Flowers Started | Flowers Ended | In Bloom |
|---|---|---|---|
| 3 | 2 | 0 | 2 |
| 3 | 2 | 0 | 2 |
| 2 | 1 | 0 | 1 |
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n log n + m log m) | Sorting starts and ends takes O(m log m), binary search for each person takes O(n log m) |
| Space | O(n + m) | Storing sorted start and end arrays |
Sorting and binary search allow the algorithm to handle large inputs efficiently.
Test Cases
# provided examples
assert Solution().fullBloomFlowers([[1,6],[3,7],[9,12],[4,13]], [2,3,7,11]) == [1,2,2,2] # example 1
assert Solution().fullBloomFlowers([[1,10],[3,3]], [3,3,2]) == [2,2,1] # example 2
# boundary cases
assert Solution().fullBloomFlowers([[1,1]], [1]) == [1] # flower blooms exactly at arrival
assert Solution().fullBloomFlowers([[1,2]], [3]) == [0] # person arrives after bloom
assert Solution().fullBloomFlowers([[5,10],[1,3]], [4,5,10]) == [0,1,1] # mix before and during blooms
# multiple flowers starting and ending at same time
assert Solution().fullBloomFlowers([[1,5],[1,5],[1,5]], [3,5,6]) == [3,3,0]
# large number test
assert Solution().fullBloomFlowers([[1,1000000000]]*50000, [1,500000000,1000000000]) == [50000,50000,50000]
| Test | Why |
|---|---|
| Example 1 | Validates normal multiple overlaps |
| Example 2 | Validates single-day bloom and duplicates |
| Boundary single bloom | Checks edge of inclusivity |
| Person after bloom | Checks proper subtraction of ended flowers |
| Multiple identical flowers | Validates counting of duplicates |
| Large number | Stress test for performance |
Edge Cases
First, a person arriving before any flower blooms. The algorithm correctly counts zero started flowers and zero ended flowers, resulting in zero in bloom.
Second, a person arriving exactly at a flower’s start or end time. Since the start is inclusive, the binary search counts this flower as started. End times are handled as < p, so an end at p does not reduce the count, which preserves inclusivity.
Third, multiple flowers starting and ending at the same time. Sorting ensures that binary search counts all flowers consistently, even if their intervals overlap exactly, so duplicates do not cause off-by-one errors.
This algorithm correctly handles all these edge cases due to the careful use of inclusive/exclusive comparisons combined with sorted binary searches.