Given an empty set of intervals, implement a data structure that can:
Implement the CountIntervals
class:
CountIntervals()
Initializes the object with an empty set of intervals.void add(int left, int right)
Adds the interval [left, right]
to the set of intervals.int count()
Returns the number of integers that are present in at least one interval.Note that an interval [left, right]
denotes all the integers x
where left <= x <= right
.
For example:
Input
["CountIntervals", "add", "add", "count", "add", "count"]
[[], [2, 3], [7, 10], [], [5, 8], []]
Output
[null, null, null, 6, null, 8]
Explanation
CountIntervals countIntervals = new CountIntervals(); // initialize the object with an empty set of intervals.
countIntervals.add(2, 3); // add [2, 3] to the set of intervals.
countIntervals.add(7, 10); // add [7, 10] to the set of intervals.
countIntervals.count(); // return 6
// the integers 2 and 3 are present in the interval [2, 3].
// the integers 7, 8, 9, and 10 are present in the interval [7, 10].
countIntervals.add(5, 8); // add [5, 8] to the set of intervals.
countIntervals.count(); // return 8
// the integers 2 and 3 are present in the interval [2, 3].
// the integers 5 and 6 are present in the interval [5, 8].
// the integers 7 and 8 are present in the intervals [5, 8] and [7, 10].
// the integers 9 and 10 are present in the interval [7, 10].
What would be an efficient data structure and algorithm to solve this problem?
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force method to count integers in intervals means we meticulously check every number within the given ranges. We look at each interval individually and see which whole numbers fall inside. Then, we simply add up the count from each interval.
Here's how the algorithm would work step-by-step:
def count_integers_in_intervals_brute_force(intervals):
count = 0
seen_integers = set()
for interval in intervals:
start_of_interval = interval[0]
end_of_interval = interval[1]
# Iterate through the integers in the interval.
for integer in range(start_of_interval, end_of_interval + 1):
# Ensures we only count each integer once.
if integer not in seen_integers:
count += 1
seen_integers.add(integer)
return count
The core idea is to maintain a collection of non-overlapping intervals, merging them when new intervals overlap existing ones. By keeping track of the merged intervals, we can easily count the number of integers covered and efficiently update the collection.
Here's how the algorithm would work step-by-step:
class CountIntervals:
def __init__(self):
self.intervals = []
def add(self, left_bound: int, right_bound: int) -> None:
new_interval = [left_bound, right_bound]
index = 0
while index < len(self.intervals):
# Check for overlaps and merge intervals if necessary
if new_interval[1] < self.intervals[index][0]:
break
elif new_interval[0] > self.intervals[index][1]:
index += 1
continue
else:
# Intervals overlap, merge them
new_interval[0] = min(new_interval[0], self.intervals[index][0])
new_interval[1] = max(new_interval[1], self.intervals[index][1])
self.intervals.pop(index)
continue
self.intervals.insert(index, new_interval)
def count(self) -> int:
total_count = 0
# Calculate the total length of all intervals
for interval in self.intervals:
total_count += (interval[1] - interval[0] + 1)
return total_count
# Your CountIntervals object will be instantiated and called as such:
# count_intervals = CountIntervals()
# count_intervals.add(left, right)
# result = count_intervals.count()
Case | How to Handle |
---|---|
Null or invalid input intervals (e.g., null array, intervals with start > end) | Throw an IllegalArgumentException or return an appropriate error code to indicate invalid input. |
Empty intervals array | Initialize count to 0 as there are no intervals present. |
Intervals with identical start and end points (single-point intervals) | These intervals should be treated as valid and contribute 1 to the count. |
Overlapping intervals | Merge overlapping intervals to avoid double-counting integers. |
Intervals with negative numbers or zero | The data structure and algorithm should correctly handle negative or zero values within the intervals. |
Large intervals spanning a significant range of integers, potentially causing memory issues if storing all integers | Use interval merging to efficiently represent the covered range without explicitly storing each integer, and consider segment trees. |
Maximum number of intervals, exceeding memory limitations | Consider using an external sorting algorithm or a disk-based data structure to handle a large number of intervals if memory is a constraint. |
Integer overflow when calculating the count of integers within intervals | Use long data type or appropriate overflow checks to prevent integer overflow during count calculations. |