2251. Number of Flowers in Full Bloom
Problem Description
You have information about when flowers bloom and when people arrive to see them.
Given:
- A 2D array
flowers
where eachflowers[i] = [start_i, end_i]
represents that the i-th flower blooms from daystart_i
to dayend_i
(both days inclusive) - An array
people
wherepeople[i]
represents the day when the i-th person arrives
You need to find out how many flowers are in full bloom when each person arrives.
For example, if a flower blooms from day 3 to day 7, and a person arrives on day 5, that flower would be counted as blooming for that person. If another flower blooms from day 1 to day 4, it would not be counted for this person since it has already wilted by day 5.
The solution uses a clever approach:
- Create two sorted lists: one with all bloom start times and one with all bloom end times
- For each person arriving at time
p
:- Count how many flowers have started blooming by time
p
usingbisect_right(start, p)
- Count how many flowers have already finished blooming before time
p
usingbisect_left(end, p)
- The difference gives the number of flowers currently in bloom
- Count how many flowers have started blooming by time
The key insight is that bisect_right(start, p)
counts all flowers that started at or before time p
, while bisect_left(end, p)
counts all flowers that ended strictly before time p
. The difference between these two values gives the exact count of flowers blooming at time p
.
Intuition
Think about what it means for a flower to be blooming at a specific time. A flower is blooming at time t
if:
- It has already started blooming (its start time ≤
t
) - It hasn't finished blooming yet (its end time ≥
t
)
This naturally leads us to think about counting flowers in two groups:
- Flowers that have started by time
t
- Flowers that have ended before time
t
The number of flowers blooming at time t
= (flowers that have started) - (flowers that have already ended)
Instead of checking each flower individually for each person (which would be inefficient), we can preprocess the data. By separating all start times and end times into two sorted arrays, we can quickly answer "how many flowers have started/ended by time t
" using binary search.
The trick with using bisect_right
for starts and bisect_left
for ends handles the boundary cases correctly:
bisect_right(start, p)
counts all flowers wherestart_time <= p
bisect_left(end, p)
counts all flowers whereend_time < p
This is important because a flower that ends on day p
should still be counted as blooming on day p
(since the end time is inclusive). By using bisect_left
for end times, we only exclude flowers that ended strictly before day p
.
This transforms a potentially complex interval overlap problem into a simple counting problem using sorted arrays and binary search, achieving an efficient solution.
Learn more about Binary Search, Prefix Sum and Sorting patterns.
Solution Approach
The solution implements the sorting and binary search strategy to efficiently count blooming flowers for each person.
Step 1: Extract and Sort Start/End Times
start, end = sorted(a for a, _ in flowers), sorted(b for _, b in flowers)
- Extract all start times from
flowers
array and sort them:start = [start_1, start_2, ..., start_n]
- Extract all end times from
flowers
array and sort them:end = [end_1, end_2, ..., end_n]
- Sorting enables binary search for efficient lookups
Step 2: Calculate Blooming Flowers for Each Person
return [bisect_right(start, p) - bisect_left(end, p) for p in people]
For each person arriving at time p
:
-
Count flowers that have started blooming:
bisect_right(start, p)
returns the position wherep
would be inserted to keep the array sorted, counting from the right- This gives us the count of all flowers where
start_time <= p
- For example, if
start = [1, 3, 5, 7]
andp = 4
,bisect_right
returns 2 (flowers starting at times 1 and 3)
-
Count flowers that have finished blooming:
bisect_left(end, p)
returns the position wherep
would be inserted to keep the array sorted, counting from the left- This gives us the count of all flowers where
end_time < p
- For example, if
end = [2, 4, 6, 8]
andp = 4
,bisect_left
returns 1 (only the flower ending at time 2 has finished)
-
Calculate the difference:
bisect_right(start, p) - bisect_left(end, p)
gives the number of flowers currently blooming- This works because flowers blooming at time
p
are those that have started but haven't ended yet
Time Complexity: O((m + n) log m)
where m
is the number of flowers and n
is the number of people
- Sorting takes
O(m log m)
- Each binary search takes
O(log m)
, performedn
times
Space Complexity: O(m)
for storing the sorted start and end arrays
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to understand how the solution works.
Given:
flowers = [[1,6], [3,7], [9,12], [4,13]]
people = [2, 3, 7, 11]
Step 1: Extract and sort start/end times
From the flowers array, we extract:
- Start times:
[1, 3, 9, 4]
→ Sorted:[1, 3, 4, 9]
- End times:
[6, 7, 12, 13]
→ Sorted:[6, 7, 12, 13]
Step 2: Process each person
For person at day 2:
bisect_right(start=[1,3,4,9], p=2)
= 1- Only flower starting at day 1 has begun (1 ≤ 2)
bisect_left(end=[6,7,12,13], p=2)
= 0- No flowers have ended yet (no end times < 2)
- Blooming flowers: 1 - 0 = 1
For person at day 3:
bisect_right(start=[1,3,4,9], p=3)
= 2- Flowers starting at days 1 and 3 have begun (both ≤ 3)
bisect_left(end=[6,7,12,13], p=3)
= 0- No flowers have ended yet (no end times < 3)
- Blooming flowers: 2 - 0 = 2
For person at day 7:
bisect_right(start=[1,3,4,9], p=7)
= 3- Flowers starting at days 1, 3, and 4 have begun (all ≤ 7)
bisect_left(end=[6,7,12,13], p=7)
= 1- Only the flower ending at day 6 has finished (6 < 7)
- Note: The flower ending at day 7 is still blooming!
- Blooming flowers: 3 - 1 = 2
For person at day 11:
bisect_right(start=[1,3,4,9], p=11)
= 4- All flowers have started (all start times ≤ 11)
bisect_left(end=[6,7,12,13], p=11)
= 2- Flowers ending at days 6 and 7 have finished (both < 11)
- Blooming flowers: 4 - 2 = 2
Result: [1, 2, 2, 2]
The key insight is how bisect_left
for end times ensures flowers ending on the same day as a person's arrival are still counted as blooming, while bisect_right
for start times includes flowers that start on the arrival day.
Solution Implementation
1from typing import List
2from bisect import bisect_right, bisect_left
3
4class Solution:
5 def fullBloomFlowers(
6 self, flowers: List[List[int]], people: List[int]
7 ) -> List[int]:
8 """
9 Calculate the number of flowers in bloom for each person's arrival time.
10
11 Args:
12 flowers: List of [start_time, end_time] representing bloom periods
13 people: List of arrival times for each person
14
15 Returns:
16 List of flower counts for each person
17 """
18 # Extract and sort all bloom start times
19 bloom_starts = sorted(start_time for start_time, _ in flowers)
20
21 # Extract and sort all bloom end times
22 bloom_ends = sorted(end_time for _, end_time in flowers)
23
24 # For each person, calculate flowers in bloom at their arrival time
25 result = []
26 for arrival_time in people:
27 # Count flowers that have started blooming by this time
28 flowers_started = bisect_right(bloom_starts, arrival_time)
29
30 # Count flowers that have already finished blooming before this time
31 flowers_ended = bisect_left(bloom_ends, arrival_time)
32
33 # The difference gives us flowers currently in bloom
34 flowers_in_bloom = flowers_started - flowers_ended
35 result.append(flowers_in_bloom)
36
37 return result
38
1class Solution {
2 /**
3 * Counts the number of flowers in full bloom for each person's arrival time.
4 *
5 * @param flowers 2D array where flowers[i] = [start_i, end_i] represents bloom period
6 * @param people array of arrival times for each person
7 * @return array where ans[i] is the number of flowers in bloom when person i arrives
8 */
9 public int[] fullBloomFlowers(int[][] flowers, int[] people) {
10 int flowerCount = flowers.length;
11
12 // Separate start and end times of all flowers
13 int[] startTimes = new int[flowerCount];
14 int[] endTimes = new int[flowerCount];
15
16 for (int i = 0; i < flowerCount; i++) {
17 startTimes[i] = flowers[i][0];
18 endTimes[i] = flowers[i][1];
19 }
20
21 // Sort both arrays to enable binary search
22 Arrays.sort(startTimes);
23 Arrays.sort(endTimes);
24
25 int peopleCount = people.length;
26 int[] result = new int[peopleCount];
27
28 // For each person, calculate flowers in bloom at their arrival time
29 for (int i = 0; i < peopleCount; i++) {
30 // Count flowers that have started blooming by time people[i]
31 int flowersStarted = binarySearch(startTimes, people[i] + 1);
32 // Count flowers that have finished blooming before time people[i]
33 int flowersEnded = binarySearch(endTimes, people[i]);
34 // The difference gives us flowers currently in bloom
35 result[i] = flowersStarted - flowersEnded;
36 }
37
38 return result;
39 }
40
41 /**
42 * Binary search to find the leftmost position where we can insert target.
43 * Returns the count of elements strictly less than target.
44 *
45 * @param nums sorted array to search in
46 * @param target value to search for
47 * @return number of elements less than target
48 */
49 private int binarySearch(int[] nums, int target) {
50 int left = 0;
51 int right = nums.length;
52
53 while (left < right) {
54 int mid = (left + right) >> 1; // Equivalent to (left + right) / 2
55
56 if (nums[mid] >= target) {
57 // Target should be at mid or before, search left half
58 right = mid;
59 } else {
60 // Target should be after mid, search right half
61 left = mid + 1;
62 }
63 }
64
65 return left;
66 }
67}
68
1class Solution {
2public:
3 vector<int> fullBloomFlowers(vector<vector<int>>& flowers, vector<int>& people) {
4 int n = flowers.size();
5
6 // Extract start and end times of each flower's bloom period
7 vector<int> startTimes;
8 vector<int> endTimes;
9
10 for (const auto& flower : flowers) {
11 startTimes.push_back(flower[0]);
12 endTimes.push_back(flower[1]);
13 }
14
15 // Sort both arrays to enable binary search
16 sort(startTimes.begin(), startTimes.end());
17 sort(endTimes.begin(), endTimes.end());
18
19 vector<int> result;
20
21 // For each person, calculate how many flowers are in bloom
22 for (const auto& personTime : people) {
23 // Count flowers that have started blooming by this time
24 // upper_bound returns iterator to first element > personTime
25 int flowersStarted = upper_bound(startTimes.begin(), startTimes.end(), personTime)
26 - startTimes.begin();
27
28 // Count flowers that have already finished blooming before this time
29 // lower_bound returns iterator to first element >= personTime
30 int flowersEnded = lower_bound(endTimes.begin(), endTimes.end(), personTime)
31 - endTimes.begin();
32
33 // The difference gives us the number of flowers currently in bloom
34 result.push_back(flowersStarted - flowersEnded);
35 }
36
37 return result;
38 }
39};
40
1/**
2 * Calculates the number of flowers in full bloom for each person's arrival time
3 * @param flowers - Array of flower bloom periods, where each element is [start, end]
4 * @param people - Array of arrival times for each person
5 * @returns Array of flower counts for each person
6 */
7function fullBloomFlowers(flowers: number[][], people: number[]): number[] {
8 const flowerCount = flowers.length;
9
10 // Extract start and end times into separate arrays
11 const startTimes = new Array(flowerCount).fill(0);
12 const endTimes = new Array(flowerCount).fill(0);
13
14 for (let i = 0; i < flowerCount; ++i) {
15 startTimes[i] = flowers[i][0];
16 endTimes[i] = flowers[i][1];
17 }
18
19 // Sort both arrays to enable binary search
20 startTimes.sort((a, b) => a - b);
21 endTimes.sort((a, b) => a - b);
22
23 const result: number[] = [];
24
25 // For each person, calculate flowers in bloom at their arrival time
26 for (const arrivalTime of people) {
27 // Count flowers that have started blooming by arrival time
28 const flowersStarted = binarySearch(startTimes, arrivalTime + 1);
29 // Count flowers that have finished blooming before arrival time
30 const flowersEnded = binarySearch(endTimes, arrivalTime);
31 // The difference gives us flowers currently in bloom
32 result.push(flowersStarted - flowersEnded);
33 }
34
35 return result;
36}
37
38/**
39 * Binary search to find the leftmost position where target can be inserted
40 * @param nums - Sorted array of numbers
41 * @param target - Target value to search for
42 * @returns Index of the first element >= target (or array length if all elements < target)
43 */
44function search(nums: number[], target: number): number {
45 let left = 0;
46 let right = nums.length;
47
48 while (left < right) {
49 const mid = (left + right) >> 1; // Equivalent to Math.floor((left + right) / 2)
50
51 if (nums[mid] >= target) {
52 // Target could be at mid or to the left
53 right = mid;
54 } else {
55 // Target must be to the right of mid
56 left = mid + 1;
57 }
58 }
59
60 return left;
61}
62
63// Alias for better naming clarity
64const binarySearch = search;
65
Time and Space Complexity
Time Complexity: O((m + n) × log n)
The time complexity breaks down as follows:
- Sorting the start times:
O(n log n)
wheren
is the number of flowers - Sorting the end times:
O(n log n)
wheren
is the number of flowers - For each person in
people
, we perform:bisect_right(start, p)
:O(log n)
binary searchbisect_left(end, p)
:O(log n)
binary search- Since there are
m
people, this step takesO(m × log n)
- Total:
O(n log n + n log n + m × log n)
=O((m + n) × log n)
Space Complexity: O(n)
The space complexity consists of:
start
array:O(n)
space to store all flower start timesend
array:O(n)
space to store all flower end times- The result list uses
O(m)
space but is not counted as auxiliary space since it's the output - Total auxiliary space:
O(n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Confusing bisect_left and bisect_right usage
The most critical pitfall is incorrectly using bisect_left
and bisect_right
, which leads to off-by-one errors in counting blooming flowers.
Incorrect Implementation:
# WRONG: Using bisect_left for both or swapping them flowers_started = bisect_left(bloom_starts, arrival_time) # Misses flowers starting exactly at arrival_time flowers_ended = bisect_right(bloom_ends, arrival_time) # Counts flowers ending at arrival_time as already ended
Why this fails:
- If a flower starts exactly at the arrival time, it should be counted as blooming
- If a flower ends exactly at the arrival time, it should still be counted as blooming (inclusive end time)
- Using
bisect_left
for starts would miss flowers that begin exactly when the person arrives - Using
bisect_right
for ends would incorrectly exclude flowers that end on the arrival day
Correct Implementation:
# RIGHT: bisect_right for starts, bisect_left for ends flowers_started = bisect_right(bloom_starts, arrival_time) # Counts all flowers with start_time <= arrival_time flowers_ended = bisect_left(bloom_ends, arrival_time) # Counts all flowers with end_time < arrival_time
2. Not handling the inclusive nature of bloom periods
Another pitfall is forgetting that bloom periods are inclusive on both ends.
Example scenario:
- Flower blooms from day 3 to day 5 (inclusive)
- Person arrives on day 5
- The flower should be counted as blooming
Test case to validate:
flowers = [[3, 5], [1, 2]] people = [5, 2] # Expected output: [1, 1] # Day 5: Only flower [3, 5] is blooming # Day 2: Only flower [1, 2] is blooming
3. Memory inefficiency with the compact one-liner
While the one-liner solution is elegant, it can be less memory efficient:
Potentially inefficient:
# Creates intermediate lists in memory
start, end = sorted(a for a, _ in flowers), sorted(b for _, b in flowers)
More memory-efficient alternative:
# Extracts values more directly
start = sorted([flower[0] for flower in flowers])
end = sorted([flower[1] for flower in flowers])
4. Not considering edge cases
Edge cases to handle:
- Empty flowers array: Should return all zeros
- Single-day bloom periods: Where start_time equals end_time
- Overlapping bloom periods: Multiple flowers blooming at the same time
Robust solution with edge case handling:
def fullBloomFlowers(self, flowers: List[List[int]], people: List[int]) -> List[int]:
# Handle empty flowers array
if not flowers:
return [0] * len(people)
bloom_starts = sorted(start for start, _ in flowers)
bloom_ends = sorted(end for _, end in flowers)
result = []
for arrival_time in people:
flowers_started = bisect_right(bloom_starts, arrival_time)
flowers_ended = bisect_left(bloom_ends, arrival_time)
result.append(flowers_started - flowers_ended)
return result
Which of the following array represent a max heap?
Recommended Readings
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Want a Structured Path to Master System Design Too? Don’t Miss This!