Skip to content

Commit 5f4becb

Browse files
committed
feat(algorithms, intervals, flowers bloom): full bloom flowers
1 parent 7d2e851 commit 5f4becb

File tree

4 files changed

+149
-3
lines changed

4 files changed

+149
-3
lines changed

algorithms/intervals/count_days/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,12 @@ def count_days(days: int, meetings: List[List[int]]) -> int:
5757
def count_days_2(days: int, meetings: List[List[int]]) -> int:
5858
"""
5959
Counts the number of days the employee is available for work but has no scheduled meetings.
60-
60+
6161
This implementation merges overlapping meetings and counts total occupied days.
62-
62+
6363
Time Complexity: O(n log n) due to sorting
6464
Space Complexity: O(1) excluding sort overhead
65-
65+
6666
Args:
6767
days (int): The total number of days the employee is available for work
6868
meetings (List[List[int]]): A list of meetings, where each meeting is represented as a list of two integers [start, end]
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Number of Flowers in Full Bloom
2+
3+
You are given a 0-indexed 2D integer array, flowers, where each element flowers[i]=[starti ,endi] represents the time
4+
interval during which the ith flower is in full bloom (inclusive).
5+
6+
You are also given a 0-indexed integer array, people, of size n, where people[i] denotes the time at which the ith person
7+
arrives to view the flowers. For each person, determine how many flowers are in full bloom at their arrival time.
8+
Return an integer array, ans, of length n, where ans[i] is the number of blooming flowers when the ith person arrives.
9+
10+
## Constraints
11+
12+
- 1 <= flowers.length <= 10^3
13+
- `flowers[i].length` == 2
14+
- 1 <= starti <= endi <= 10^4
15+
- 1 <= people.length <= 10^3
16+
- 1 <= people[i] <= 10^4
17+
18+
### Solution
19+
20+
The core intuition behind this solution is to avoid directly simulating flower blooming across all possible time values,
21+
which would be inefficient. Instead, we transform the problem into one of counting intervals using binary search. The
22+
challenge is to determine a person’s arrival time. The difference between these two counts gives the number of flowers
23+
in bloom at that moment.
24+
25+
To achieve this, the solution separates the flowers’ intervals into two lists: one for start times and one for end times.
26+
The key trick is that the end times are stored as endi+1 instead of endi. This ensures that when a person arrives at
27+
exactly endi, the flower is still counted as blooming. With both lists sorted, we can use binary search to quickly
28+
determine counts for any arrival time.
29+
30+
Let’s break down the key steps of the solution:
31+
32+
1. Initialize the starts and ends arrays for storing start and end times, respectively.
33+
2. Iterate over each flower interval [starti, endi].
34+
- Add starti to the starts list.
35+
- Add endi+1 to the ends list.
36+
3. Sort both lists to ensure that binary search can efficiently count the number of flowers that started or ended before
37+
a given arrival time.
38+
4. Create an ans list of size n to store the answer for each person.
39+
5. Iterate over people array and for each person’s arrival time, perform two binary searches:
40+
- On starts, find how many flowers began blooming at or before the arrival time.
41+
- On ends, find how many flowers had already finished blooming before the arrival time.
42+
- Store the difference between the counts in the ans array.
43+
6. After completing the iteration, return the ans array as the output.
44+
45+
Binary Search implementation details:
46+
- The binarySearch function is a modified version of the standard algorithm.
47+
- It returns the index of the first element greater than the target (upper bound).
48+
- This effectively gives the count of values ≤ target.
49+
50+
### Time Complexity
51+
52+
- Creating the starts and ends arrays of length n takes O(n) time.
53+
- Sorting both arrays costs O(nlogn).
54+
- Next, for each of the m people, we perform two binary searches on these arrays, each taking O(logn) time. This
55+
contributes O(mlogn) in total.
56+
57+
So, the overall time complexity is O(nlogn+mlogn), which simplifies to O((n+m)logn).
58+
59+
### Space Complexity
60+
61+
The space complexity is O(n) because the solution stores all n flowers’ start and end times in separate lists (starts
62+
and ends). So, the overall space complexity is O(n).
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
from typing import List
2+
from bisect import bisect_right, bisect_left
3+
4+
5+
def full_bloom_flowers(flowers: List[List[int]], people: List[int]) -> List[int]:
6+
# if there are no flowers, then we can return an empty list
7+
if not flowers:
8+
return []
9+
10+
# sorted list of start and end times for the periods of blooming flowers
11+
start_times = sorted([start for start, _ in flowers])
12+
end_times = sorted([end for _, end in flowers])
13+
14+
# This keeps track of the number of flowers that are in full bloom at each person's arrival time
15+
result = []
16+
17+
for persons_arrival_time in people:
18+
# Counts every flower that started at or before the arrival
19+
flowers_in_bloom_before_arrival = bisect_right(
20+
start_times, persons_arrival_time
21+
)
22+
# Counts only flowers that ended strictly before the arrival, leaving the ones ending at that time in the "blooming" tally.
23+
flowers_not_in_bloom_before_arrival = bisect_left(
24+
end_times, persons_arrival_time
25+
)
26+
count = flowers_in_bloom_before_arrival - flowers_not_in_bloom_before_arrival
27+
result.append(count)
28+
29+
return result
30+
31+
32+
def full_bloom_flowers_2(flowers: List[List[int]], people: List[int]) -> List[int]:
33+
starts = []
34+
ends = []
35+
36+
for start, end in flowers:
37+
starts.append(start)
38+
ends.append(end + 1)
39+
40+
starts.sort()
41+
ends.sort()
42+
ans = []
43+
44+
for person in people:
45+
i = bisect_right(starts, person)
46+
j = bisect_right(ends, person)
47+
ans.append(i - j)
48+
49+
return ans
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import unittest
2+
from typing import List
3+
from parameterized import parameterized
4+
from algorithms.intervals.full_bloom_flowers import (
5+
full_bloom_flowers,
6+
full_bloom_flowers_2,
7+
)
8+
9+
TEST_CASES = [
10+
([[5, 5]], [5], [1]),
11+
([[3, 3], [3, 3], [3, 3]], [2, 3, 4], [0, 3, 0]),
12+
([[2, 4], [6, 8]], [1, 5, 9], [0, 0, 0]),
13+
([[1, 4], [2, 5], [3, 6]], [1, 2, 3, 4, 5, 6], [1, 2, 3, 3, 2, 1]),
14+
([[1, 2], [100, 200], [300, 400]], [1, 150, 250, 350, 500], [1, 1, 0, 1, 0]),
15+
]
16+
17+
18+
class FullBloomFlowersTestCase(unittest.TestCase):
19+
@parameterized.expand(TEST_CASES)
20+
def test_full_bloom_flowers(
21+
self, flowers: List[List[int]], people: List[int], expected: List[int]
22+
):
23+
actual = full_bloom_flowers(flowers, people)
24+
self.assertEqual(expected, actual)
25+
26+
@parameterized.expand(TEST_CASES)
27+
def test_full_bloom_flowers(
28+
self, flowers: List[List[int]], people: List[int], expected: List[int]
29+
):
30+
actual = full_bloom_flowers_2(flowers, people)
31+
self.assertEqual(expected, actual)
32+
33+
34+
if __name__ == "__main__":
35+
unittest.main()

0 commit comments

Comments
 (0)