From 5f4becb8e452c6f24b4996f7cc5cdf94a8fb6e8a Mon Sep 17 00:00:00 2001 From: Lusina <12752833+BrianLusina@users.noreply.github.com> Date: Thu, 25 Dec 2025 09:52:33 +0300 Subject: [PATCH 1/3] feat(algorithms, intervals, flowers bloom): full bloom flowers --- algorithms/intervals/count_days/__init__.py | 6 +- .../intervals/full_bloom_flowers/README.md | 62 +++++++++++++++++++ .../intervals/full_bloom_flowers/__init__.py | 49 +++++++++++++++ .../test_full_bloom_flowers.py | 35 +++++++++++ 4 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 algorithms/intervals/full_bloom_flowers/README.md create mode 100644 algorithms/intervals/full_bloom_flowers/__init__.py create mode 100644 algorithms/intervals/full_bloom_flowers/test_full_bloom_flowers.py diff --git a/algorithms/intervals/count_days/__init__.py b/algorithms/intervals/count_days/__init__.py index a820db5e..5fec785c 100644 --- a/algorithms/intervals/count_days/__init__.py +++ b/algorithms/intervals/count_days/__init__.py @@ -57,12 +57,12 @@ def count_days(days: int, meetings: List[List[int]]) -> int: def count_days_2(days: int, meetings: List[List[int]]) -> int: """ Counts the number of days the employee is available for work but has no scheduled meetings. - + This implementation merges overlapping meetings and counts total occupied days. - + Time Complexity: O(n log n) due to sorting Space Complexity: O(1) excluding sort overhead - + Args: days (int): The total number of days the employee is available for work meetings (List[List[int]]): A list of meetings, where each meeting is represented as a list of two integers [start, end] diff --git a/algorithms/intervals/full_bloom_flowers/README.md b/algorithms/intervals/full_bloom_flowers/README.md new file mode 100644 index 00000000..0c1d9703 --- /dev/null +++ b/algorithms/intervals/full_bloom_flowers/README.md @@ -0,0 +1,62 @@ +# Number of Flowers in Full Bloom + +You are given a 0-indexed 2D integer array, flowers, where each element flowers[i]=[starti ,endi] represents the time +interval during which the ith flower is in full bloom (inclusive). + +You are also given a 0-indexed integer array, people, of size n, where people[i] denotes the time at which the ith person +arrives to view the flowers. For each person, determine how many flowers are in full bloom at their arrival time. +Return an integer array, ans, of length n, where ans[i] is the number of blooming flowers when the ith person arrives. + +## Constraints + +- 1 <= flowers.length <= 10^3 +- `flowers[i].length` == 2 +- 1 <= starti <= endi <= 10^4 +- 1 <= people.length <= 10^3 +- 1 <= people[i] <= 10^4 + +### Solution + +The core intuition behind this solution is to avoid directly simulating flower blooming across all possible time values, +which would be inefficient. Instead, we transform the problem into one of counting intervals using binary search. The +challenge is to determine a person’s arrival time. The difference between these two counts gives the number of flowers +in bloom at that moment. + +To achieve this, the solution separates the flowers’ intervals into two lists: one for start times and one for end times. +The key trick is that the end times are stored as endi+1 instead of endi. This ensures that when a person arrives at +exactly endi, the flower is still counted as blooming. With both lists sorted, we can use binary search to quickly +determine counts for any arrival time. + +Let’s break down the key steps of the solution: + +1. Initialize the starts and ends arrays for storing start and end times, respectively. +2. Iterate over each flower interval [starti, endi]. + - Add starti to the starts list. + - Add endi+1 to the ends list. +3. Sort both lists to ensure that binary search can efficiently count the number of flowers that started or ended before + a given arrival time. +4. Create an ans list of size n to store the answer for each person. +5. Iterate over people array and for each person’s arrival time, perform two binary searches: + - On starts, find how many flowers began blooming at or before the arrival time. + - On ends, find how many flowers had already finished blooming before the arrival time. + - Store the difference between the counts in the ans array. +6. After completing the iteration, return the ans array as the output. + +Binary Search implementation details: +- The binarySearch function is a modified version of the standard algorithm. +- It returns the index of the first element greater than the target (upper bound). +- This effectively gives the count of values ≤ target. + +### Time Complexity + +- Creating the starts and ends arrays of length n takes O(n) time. +- Sorting both arrays costs O(nlogn). +- Next, for each of the m people, we perform two binary searches on these arrays, each taking O(logn) time. This +contributes O(mlogn) in total. + +So, the overall time complexity is O(nlogn+mlogn), which simplifies to O((n+m)logn). + +### Space Complexity + +The space complexity is O(n) because the solution stores all n flowers’ start and end times in separate lists (starts +and ends). So, the overall space complexity is O(n). diff --git a/algorithms/intervals/full_bloom_flowers/__init__.py b/algorithms/intervals/full_bloom_flowers/__init__.py new file mode 100644 index 00000000..d961d7ec --- /dev/null +++ b/algorithms/intervals/full_bloom_flowers/__init__.py @@ -0,0 +1,49 @@ +from typing import List +from bisect import bisect_right, bisect_left + + +def full_bloom_flowers(flowers: List[List[int]], people: List[int]) -> List[int]: + # if there are no flowers, then we can return an empty list + if not flowers: + return [] + + # sorted list of start and end times for the periods of blooming flowers + start_times = sorted([start for start, _ in flowers]) + end_times = sorted([end for _, end in flowers]) + + # This keeps track of the number of flowers that are in full bloom at each person's arrival time + result = [] + + for persons_arrival_time in people: + # Counts every flower that started at or before the arrival + flowers_in_bloom_before_arrival = bisect_right( + start_times, persons_arrival_time + ) + # Counts only flowers that ended strictly before the arrival, leaving the ones ending at that time in the "blooming" tally. + flowers_not_in_bloom_before_arrival = bisect_left( + end_times, persons_arrival_time + ) + count = flowers_in_bloom_before_arrival - flowers_not_in_bloom_before_arrival + result.append(count) + + return result + + +def full_bloom_flowers_2(flowers: List[List[int]], people: List[int]) -> List[int]: + starts = [] + ends = [] + + for start, end in flowers: + starts.append(start) + ends.append(end + 1) + + starts.sort() + ends.sort() + ans = [] + + for person in people: + i = bisect_right(starts, person) + j = bisect_right(ends, person) + ans.append(i - j) + + return ans diff --git a/algorithms/intervals/full_bloom_flowers/test_full_bloom_flowers.py b/algorithms/intervals/full_bloom_flowers/test_full_bloom_flowers.py new file mode 100644 index 00000000..24f72f6a --- /dev/null +++ b/algorithms/intervals/full_bloom_flowers/test_full_bloom_flowers.py @@ -0,0 +1,35 @@ +import unittest +from typing import List +from parameterized import parameterized +from algorithms.intervals.full_bloom_flowers import ( + full_bloom_flowers, + full_bloom_flowers_2, +) + +TEST_CASES = [ + ([[5, 5]], [5], [1]), + ([[3, 3], [3, 3], [3, 3]], [2, 3, 4], [0, 3, 0]), + ([[2, 4], [6, 8]], [1, 5, 9], [0, 0, 0]), + ([[1, 4], [2, 5], [3, 6]], [1, 2, 3, 4, 5, 6], [1, 2, 3, 3, 2, 1]), + ([[1, 2], [100, 200], [300, 400]], [1, 150, 250, 350, 500], [1, 1, 0, 1, 0]), +] + + +class FullBloomFlowersTestCase(unittest.TestCase): + @parameterized.expand(TEST_CASES) + def test_full_bloom_flowers( + self, flowers: List[List[int]], people: List[int], expected: List[int] + ): + actual = full_bloom_flowers(flowers, people) + self.assertEqual(expected, actual) + + @parameterized.expand(TEST_CASES) + def test_full_bloom_flowers( + self, flowers: List[List[int]], people: List[int], expected: List[int] + ): + actual = full_bloom_flowers_2(flowers, people) + self.assertEqual(expected, actual) + + +if __name__ == "__main__": + unittest.main() From 1c52afbbdb5eea18cecaead76cb1aff8c7167e80 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Thu, 25 Dec 2025 06:52:52 +0000 Subject: [PATCH 2/3] updating DIRECTORY.md --- DIRECTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index eae3cf76..08e23726 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -103,6 +103,8 @@ * Intervals * Count Days * [Test Count Days Without Meetings](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/intervals/count_days/test_count_days_without_meetings.py) + * Full Bloom Flowers + * [Test Full Bloom Flowers](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/intervals/full_bloom_flowers/test_full_bloom_flowers.py) * Insert Interval * [Test Insert Interval](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/intervals/insert_interval/test_insert_interval.py) * Interval Intersection From fed8eafd9593e25359dc9a8da5d55b557c1bdcc6 Mon Sep 17 00:00:00 2001 From: Lusina <12752833+BrianLusina@users.noreply.github.com> Date: Fri, 26 Dec 2025 09:04:37 +0300 Subject: [PATCH 3/3] test(algorithms, intervals): full bloom flowers Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../intervals/full_bloom_flowers/test_full_bloom_flowers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/algorithms/intervals/full_bloom_flowers/test_full_bloom_flowers.py b/algorithms/intervals/full_bloom_flowers/test_full_bloom_flowers.py index 24f72f6a..e9b218c2 100644 --- a/algorithms/intervals/full_bloom_flowers/test_full_bloom_flowers.py +++ b/algorithms/intervals/full_bloom_flowers/test_full_bloom_flowers.py @@ -24,7 +24,7 @@ def test_full_bloom_flowers( self.assertEqual(expected, actual) @parameterized.expand(TEST_CASES) - def test_full_bloom_flowers( + def test_full_bloom_flowers_2( self, flowers: List[List[int]], people: List[int], expected: List[int] ): actual = full_bloom_flowers_2(flowers, people)