Design a data structure to manage a set of intervals and efficiently count the number of integers covered by these intervals. Implement the CountIntervals
class with the following methods:
CountIntervals()
: Initializes an empty set of intervals.void add(int left, int right)
: Adds the interval [left, right]
to the set.int count()
: Returns the number of integers present in at least one interval.Note: An interval [left, right]
includes all integers x
where left <= x <= right
.
Example:
CountIntervals countIntervals = new CountIntervals();
countIntervals.add(2, 3); // Adds interval [2, 3]
countIntervals.add(7, 10); // Adds interval [7, 10]
countIntervals.count(); // Returns 6 (integers 2, 3, 7, 8, 9, 10)
countIntervals.add(5, 8); // Adds interval [5, 8]
countIntervals.count(); // Returns 8 (integers 2, 3, 5, 6, 7, 8, 9, 10)
Explain the time and space complexity of your solution. Consider edge cases such as overlapping intervals and large input ranges (1 <= left <= right <= 10^9). At most 10^5 calls to add
and count
will be made.
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. |