Facebook Pixel

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 each flowers[i] = [start_i, end_i] represents that the i-th flower blooms from day start_i to day end_i (both days inclusive)
  • An array people where people[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:

  1. Create two sorted lists: one with all bloom start times and one with all bloom end times
  2. For each person arriving at time p:
    • Count how many flowers have started blooming by time p using bisect_right(start, p)
    • Count how many flowers have already finished blooming before time p using bisect_left(end, p)
    • The difference gives the number of flowers currently in bloom

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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. Flowers that have started by time t
  2. 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 where start_time <= p
  • bisect_left(end, p) counts all flowers where end_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 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    public int[] fullBloomFlowers(int[][] flowers, int[] people) {
3        int flowerCount = flowers.length;
4
5        int[] startTimes = new int[flowerCount];
6        int[] endTimes = new int[flowerCount];
7
8        for (int i = 0; i < flowerCount; i++) {
9            startTimes[i] = flowers[i][0];
10            endTimes[i] = flowers[i][1];
11        }
12
13        Arrays.sort(startTimes);
14        Arrays.sort(endTimes);
15
16        int peopleCount = people.length;
17        int[] result = new int[peopleCount];
18
19        for (int i = 0; i < peopleCount; i++) {
20            // Count flowers with start <= arrivalTime (using target = arrivalTime + 1)
21            int flowersStarted = bisectLeft(startTimes, people[i] + 1);
22            // Count flowers with end < arrivalTime
23            int flowersEnded = bisectLeft(endTimes, people[i]);
24            result[i] = flowersStarted - flowersEnded;
25        }
26
27        return result;
28    }
29
30    // Binary search using first_true_index template
31    // Returns first index where nums[idx] >= target (count of elements < target)
32    private int bisectLeft(int[] nums, int target) {
33        int left = 0;
34        int right = nums.length - 1;
35        int firstTrueIndex = nums.length;  // Default: no element >= target
36
37        while (left <= right) {
38            int mid = left + (right - left) / 2;
39            if (nums[mid] >= target) {
40                firstTrueIndex = mid;
41                right = mid - 1;
42            } else {
43                left = mid + 1;
44            }
45        }
46
47        return firstTrueIndex;
48    }
49}
50
1class Solution {
2public:
3    vector<int> fullBloomFlowers(vector<vector<int>>& flowers, vector<int>& people) {
4        vector<int> startTimes, endTimes;
5
6        for (const auto& flower : flowers) {
7            startTimes.push_back(flower[0]);
8            endTimes.push_back(flower[1]);
9        }
10
11        sort(startTimes.begin(), startTimes.end());
12        sort(endTimes.begin(), endTimes.end());
13
14        // Binary search using first_true_index template
15        auto bisectLeft = [](const vector<int>& nums, int target) -> int {
16            int left = 0;
17            int right = nums.size() - 1;
18            int firstTrueIndex = nums.size();
19
20            while (left <= right) {
21                int mid = left + (right - left) / 2;
22                if (nums[mid] >= target) {
23                    firstTrueIndex = mid;
24                    right = mid - 1;
25                } else {
26                    left = mid + 1;
27                }
28            }
29            return firstTrueIndex;
30        };
31
32        vector<int> result;
33
34        for (const auto& personTime : people) {
35            // Count flowers with start <= personTime
36            int flowersStarted = bisectLeft(startTimes, personTime + 1);
37            // Count flowers with end < personTime
38            int flowersEnded = bisectLeft(endTimes, personTime);
39            result.push_back(flowersStarted - flowersEnded);
40        }
41
42        return result;
43    }
44};
45
1function fullBloomFlowers(flowers: number[][], people: number[]): number[] {
2    const flowerCount = flowers.length;
3
4    const startTimes = new Array(flowerCount).fill(0);
5    const endTimes = new Array(flowerCount).fill(0);
6
7    for (let i = 0; i < flowerCount; ++i) {
8        startTimes[i] = flowers[i][0];
9        endTimes[i] = flowers[i][1];
10    }
11
12    startTimes.sort((a, b) => a - b);
13    endTimes.sort((a, b) => a - b);
14
15    // Binary search using first_true_index template
16    // Returns first index where nums[idx] >= target
17    const bisectLeft = (nums: number[], target: number): number => {
18        let left = 0;
19        let right = nums.length - 1;
20        let firstTrueIndex = nums.length;  // Default: no element >= target
21
22        while (left <= right) {
23            const mid = Math.floor((left + right) / 2);
24            if (nums[mid] >= target) {
25                firstTrueIndex = mid;
26                right = mid - 1;
27            } else {
28                left = mid + 1;
29            }
30        }
31        return firstTrueIndex;
32    };
33
34    const result: number[] = [];
35
36    for (const arrivalTime of people) {
37        // Count flowers with start <= arrivalTime
38        const flowersStarted = bisectLeft(startTimes, arrivalTime + 1);
39        // Count flowers with end < arrivalTime
40        const flowersEnded = bisectLeft(endTimes, arrivalTime);
41        result.push(flowersStarted - flowersEnded);
42    }
43
44    return result;
45}
46

Solution Approach

The solution uses sorting and binary search to count blooming flowers.

Step 1: Extract and Sort Start/End Times

bloom_starts = sorted(start for start, _ in flowers)
bloom_ends = sorted(end for _, end in flowers)

Step 2: Binary Search Using first_true_index Template

For each person arriving at time p, we count:

  • Flowers started: count of flowers with start <= p
  • Flowers ended: count of flowers with end < p

Using the first_true_index template to implement bisect_left:

def bisect_left(nums, target):
    # Find first index where nums[idx] >= target
    left, right = 0, len(nums) - 1
    first_true_index = len(nums)  # Default: no element >= target

    while left <= right:
        mid = (left + right) // 2
        if nums[mid] >= target:
            first_true_index = mid
            right = mid - 1
        else:
            left = mid + 1

    return first_true_index

Counting Logic:

  • flowers_started = bisect_left(starts, p + 1) counts flowers with start <= p
  • flowers_ended = bisect_left(ends, p) counts flowers with end < p
  • blooming = flowers_started - flowers_ended

Why p + 1 for starts? bisect_left(starts, p + 1) finds the first index where start >= p + 1, which equals the count of starts where start <= p.

Time Complexity: O((m + n) log m) where m = flowers, n = people.

Space Complexity: O(m) for sorted arrays.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example 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.

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) where n is the number of flowers
  • Sorting the end times: O(n log n) where n is the number of flowers
  • For each person in people, we perform:
    • bisect_right(start, p): O(log n) binary search
    • bisect_left(end, p): O(log n) binary search
    • Since there are m people, this step takes O(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 times
  • end 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. Using Wrong Binary Search Template

The problem uses bisect_left which finds the first index where element >= target.

Wrong approach:

while left < right:
    mid = (left + right) >> 1
    if nums[mid] >= target:
        right = mid
    else:
        left = mid + 1
return left  # Returns left directly

Correct approach (first_true_index template):

first_true_index = len(nums)  # Default
while left <= right:
    mid = (left + right) // 2
    if nums[mid] >= target:
        first_true_index = mid
        right = mid - 1
    else:
        left = mid + 1
return first_true_index

2. Confusing bisect_left vs bisect_right Logic

To count flowers with start <= p, use bisect_left(starts, p + 1) (not bisect_right). To count flowers with end < p, use bisect_left(ends, p).

The key insight:

  • bisect_left(arr, x) returns count of elements < x
  • bisect_left(arr, x + 1) returns count of elements <= x

3. Not Handling Inclusive Bloom Periods

Bloom periods are inclusive: a flower blooming [3, 5] is still blooming on day 5.

The formula bisect_left(ends, p) correctly counts only flowers with end < p, so flowers ending exactly on arrival day are still counted as blooming.

4. Incorrect Default for first_true_index

When no element >= target exists, first_true_index should default to len(nums) to correctly return "all elements are less than target".

Wrong: first_true_index = -1 Correct: first_true_index = len(nums)

Loading...
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What's the output of running the following function using the following tree as input?

1def serialize(root):
2    res = []
3    def dfs(root):
4        if not root:
5            res.append('x')
6            return
7        res.append(root.val)
8        dfs(root.left)
9        dfs(root.right)
10    dfs(root)
11    return ' '.join(res)
12
1import java.util.StringJoiner;
2
3public static String serialize(Node root) {
4    StringJoiner res = new StringJoiner(" ");
5    serializeDFS(root, res);
6    return res.toString();
7}
8
9private static void serializeDFS(Node root, StringJoiner result) {
10    if (root == null) {
11        result.add("x");
12        return;
13    }
14    result.add(Integer.toString(root.val));
15    serializeDFS(root.left, result);
16    serializeDFS(root.right, result);
17}
18
1function serialize(root) {
2    let res = [];
3    serialize_dfs(root, res);
4    return res.join(" ");
5}
6
7function serialize_dfs(root, res) {
8    if (!root) {
9        res.push("x");
10        return;
11    }
12    res.push(root.val);
13    serialize_dfs(root.left, res);
14    serialize_dfs(root.right, res);
15}
16

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More