Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions algorithms/intervals/count_days/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
62 changes: 62 additions & 0 deletions algorithms/intervals/full_bloom_flowers/README.md
Original file line number Diff line number Diff line change
@@ -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).
49 changes: 49 additions & 0 deletions algorithms/intervals/full_bloom_flowers/__init__.py
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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_2(
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()
Loading