1893. Check if All the Integers in a Range Are Covered
Problem Description
In the given problem, you are provided with an array called ranges
. This array contains subarrays where each subarray [start, end]
represents a range of numbers from start
to end
, including both start
and end
. In addition to this array, you are given two integers left
and right
, which define a range of their own. The task is to determine whether every number within the range from left
to right
(inclusive) is covered by at least one range specified by the subarrays in ranges
. If every number in the range [left, right]
is found within one or more of the given ranges
, you should return true
. Otherwise, return false
.
An integer is considered covered if it lies within any of the given ranges, inclusive of the range's endpoints. In simpler terms, you need to ensure that there are no gaps in coverage from left
to right
. If you find even one number in this range not covered by any intervals in ranges
, the answer should be false
.
Intuition
The idea behind the provided solution is to leverage a technique known as the "difference array". The difference array approach is useful in handling queries of range update operations efficiently and can be used in situations like this, where we have to add or subtract values over a range and then check the sum or coverage over a continuous range.
Here, we create an auxiliary array called diff
with extra elements (up to 52, taking into account possible values from left
and right
). Initially, this diff
array is filled with zeros. The intuition is to increase the value at the start of a range by 1
and decrease the value right after the end of a range by 1
. When we traverse through the diff
array and compute the prefix sum at each point, we can determine the total coverage at that point. The prefix sum gives us an understanding of how many ranges cover a particular number.
If we follow through with calculating the prefix sums of the diff
array, we would find that for any interval [l, r]
in ranges
, the coverage would be reflected correctly. Once we've processed all the ranges, we'll iterate through the diff
array once more. While iterating, if we encounter any place within our [left, right]
range where the cumulative coverage drops to 0
(which means it's not covered by any interval), then we immediately return false
. If we make it through the entire left
to right
range and all points are covered, we return true
.
The strength of this approach lies in efficiently updating the range coverage and then quickly assessing whether any point within the target range [left, right]
is uncovered.
Learn more about Prefix Sum patterns.
Solution Approach
The solution to this problem makes use of a simple but powerful concept called the difference array approach, which is particularly useful in scenarios involving increment and decrement operations over a range of elements.
To understand the implementation, we follow these steps:
-
We initialize a difference array
diff
with a length sufficient to cover all possible values in the problem statement. Here, we fixed its length to 52, which is an arbitrary choice to ensure that we can accommodate all ranges given that actual range requirements are not specified. Thisdiff
array tracks the net change at each point. -
We iterate over the given
ranges
array. For each range[l, r]
, we incrementdiff[l]
by1
and decrementdiff[r + 1]
by1
. The increment atdiff[l]
indicates that at pointl
, the coverage starts (or increases), and the decrement atdiff[r + 1]
indicates that right after pointr
, the coverage ends (or decreases). -
We then iterate over
diff
and compute the prefix sum at each point. We use a variablecur
to keep track of the cumulative sum as we go.- The prefix sum up to a certain index
i
in thediff
array essentially represents the number of ranges covering the point corresponding toi
.
- The prefix sum up to a certain index
-
During the same iteration, we check each position
i
against our target coverage range[left, right]
. If the prefix sum ati
(i.e.,cur
) is0
for anyi
within this interval, it means that pointi
is not covered by any range. Hence, we returnfalse
. -
If we successfully iterate over the entire
[left, right]
interval without finding any points with0
coverage, we returntrue
, since this implies that all points within the target range are covered by at least one interval inranges
.
The key algorithmic concepts used in the implementation are iteration, conditional checks, and the management of prefix sums, which allow us to track the cumulative effect of all the ranges on any given point.
This approach is efficient because each range update (increment and decrement at the start and end points of the range) is performed in constant time, and the final check for uncovered points is performed in a single pass through the diff
array.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's consider a small example to illustrate the solution approach:
Assume we have an array ranges
given as [[1, 2], [5, 6], [1, 5]]
, and we need to check if all numbers in the range [1, 6]
are covered by at least one of the intervals in ranges
.
Following the steps outlined in the solution approach:
Step 0 (Initial Setup):
- We define
left = 1
,right = 6
, and we initialize ourdiff
array of size 52 (to cover the possible range) with all0
s.
Step 1 (Updating the Difference Array):
- For the range
[1, 2]
, we incrementdiff[1]
by1
and decrementdiff[3]
by1
. - For the range
[5, 6]
, we incrementdiff[5]
by1
and decrementdiff[7]
by1
. - For the range
[1, 5]
, we incrementdiff[1]
again by1
and decrementdiff[6]
by1
.
After step 1, the starting segment of our diff
array looks like this: [0, 2, 0, -1, 0, 1, -2, 1, ... (remaining all zeros)]
.
Step 2 (Computing Prefix Sums and Checking for Coverage):
- We initialize
cur
to0
and start iterating from1
to6
(the range[left, right]
). We will compute the prefix sum and check it against our coverage requirement:- At
i = 1
:cur += diff[1]
(which is2
), socur
becomes2
. - At
i = 2
:cur += diff[2]
(which is0
), socur
remains2
. - At
i = 3
:cur += diff[3]
(which is-1
), socur
becomes1
. - At
i = 4
:cur += diff[4]
(which is0
), socur
remains1
. - At
i = 5
:cur += diff[5]
(which is1
), socur
becomes2
. - At
i = 6
:cur += diff[6]
(which is-2
), socur
becomes0
.
- At
During this iteration, we check whether cur
becomes 0
before reaching the end of our range. Here, cur
does become 0
at i = 6
, indicating that the point 6
is not covered by any interval since the cumulative sum drops to zero at this point.
Thus according to our algorithm, we would return false
, as there is at least one number (6
) in the range [left, right]
that isn't covered by any range in ranges
.
Through this small example, we've followed the difference array approach to determine whether every number within the target range is covered by the given ranges. By performing constant time updates to our diff
array and a single pass check, we efficiently arrive at our answer.
Solution Implementation
1class Solution:
2 def isCovered(self, ranges: List[List[int]], left: int, right: int) -> bool:
3 # Initialize a difference array with 52 elements, one extra to accommodate the 0-indexing and one more to handle the 'r+1' without index error.
4 difference_array = [0] * 52
5
6 # Iterate over each range in the ranges list.
7 for range_start, range_end in ranges:
8 # Increment the count at the start index of the range.
9 difference_array[range_start] += 1
10 # Decrement the count immediately after the end index of the range.
11 difference_array[range_end + 1] -= 1
12
13 # Accumulate the frequency count while traversing from start to end.
14 current_coverage = 0
15 for index, freq_change in enumerate(difference_array):
16 # Update the coverage by adding the current frequency change.
17 current_coverage += freq_change
18
19 # If the index is in the query range [left, right] and the current coverage is 0,
20 # it means this number is not covered by any range, thus return False.
21 if left <= index <= right and current_coverage == 0:
22 return False
23
24 # If the loop completed without returning False, all numbers in [left, right] are covered.
25 return True
26
1class Solution {
2 // Method to check if all integers in the range [left, right] are covered by any of the ranges
3 public boolean isCovered(int[][] ranges, int left, int right) {
4 // Array for the difference between the count of start and end points
5 int[] diff = new int[52]; // A size of 52 to cover range from 0 to 50 and to account for the end offset.
6
7 // Loop through each range in the input array and update the diff array.
8 for (int[] range : ranges) {
9 int start = range[0]; // Start of the current range
10 int end = range[1]; // End of the current range
11
12 ++diff[start]; // Increment the start index to indicate the range starts here
13 --diff[end + 1]; // Decrement the index after the end point to indicate the range ends before this index
14 }
15
16 // Variable to keep track of the current coverage status
17 int coverage = 0;
18 // Loop over the diff array and check if all numbers are covered
19 for (int i = 0; i < diff.length; ++i) {
20 coverage += diff[i]; // Add the difference to get the current number of ranges covering i
21
22 // If the current number falls within the query range and is not covered by any range, return false.
23 if (i >= left && i <= right && coverage == 0) {
24 return false;
25 }
26 }
27
28 // If we pass through the loop without returning false, all numbers in [left, right] are covered.
29 return true;
30 }
31}
32
1class Solution {
2public:
3 bool isCovered(vector<vector<int>>& ranges, int left, int right) {
4 // Creating a difference array with an extra space to avoid index out-of-bounds,
5 // since we will operate in the range of [1, 50] according to the problem's constraints
6 int diff[52] = {};
7
8 // Iterate through each range and maintain a difference array
9 for (auto& range : ranges) {
10 int rangeStart = range[0];
11 int rangeEnd = range[1];
12 // Increment at the start index, indicating the beginning of a range covering
13 ++diff[rangeStart];
14 // Decrement just after the end index, indicating the end of coverage
15 --diff[rangeEnd + 1];
16 }
17
18 // Initialize a variable to track the coverage at each point
19 int coverage = 0;
20
21 // Iterate through the hypothetical line where the points need to be covered
22 for (int i = 1; i < 52; ++i) {
23 // Apply the effect of the current index on the coverage
24 coverage += diff[i];
25 // Check if the current index falls within the range to be checked for coverage
26 if (i >= left && i <= right) {
27 // If the coverage drops to zero or below, it means this point is not covered
28 if (coverage <= 0) {
29 return false;
30 }
31 }
32 }
33
34 // If the function hasn't returned false, all points are covered
35 return true;
36 }
37};
38
1function isCovered(ranges: number[][], left: number, right: number): boolean {
2 // Create a difference array with an initially filled with zeros to track coverages.
3 const coverageDiff = new Array(52).fill(0);
4
5 // Populate the difference array using the range updates (using the prefix sum technique).
6 for (const [start, end] of ranges) {
7 ++coverageDiff[start];
8 --coverageDiff[end + 1];
9 }
10
11 // 'currentCoverage' will track the coverage of the current position by summing up values.
12 let currentCoverage = 0;
13
14 // Iterate through each position up to 51 (given the array starts at 0).
15 for (let position = 0; position < 52; ++position) {
16 // Add the coverage difference at the current position to the running total 'currentCoverage'.
17 currentCoverage += coverageDiff[position];
18
19 // If the current position is within the specified range and is not covered, return false.
20 if (position >= left && position <= right && currentCoverage <= 0) {
21 return false;
22 }
23 }
24
25 // If all positions in the specified range are covered, return true.
26 return true;
27}
28
Time and Space Complexity
The given Python code implements a difference array to determine if all the numbers in the interval [left, right]
are covered by at least one of the ranges in ranges
.
Time complexity:
The time complexity of this algorithm is determined by several steps in the code:
-
Initialize the difference array: The difference array
diff
has a fixed size of52
, so this step isO(1)
. -
Populate the difference array: For each range
[l, r]
inranges
, we perform a constant time operation to increment and decrement at positionl
andr + 1
, respectively. If there aren
ranges inranges
, this step has a complexity ofO(n)
. -
Accumulate the difference array and check coverage: We then accumulate the difference array values to get the coverage count up to each index. Since the range of the
diff
array is from0
to51
, this step isO(52)
which isO(1)
as it has a fixed size. -
Checking the interval
[left, right]
: We iterate through thediff
array, which is a fixed size, and checking if the coverage is0
for any point between[left, right]
. This is alsoO(1)
since the interval[left, right]
is within a fixed-size range.The final time complexity is the sum of the complexities of these steps:
O(n) + O(1) + O(1) + O(1)
, which simplifies toO(n)
wheren
is the number of ranges provided.
Space complexity:
The space complexity is determined by the storage used which is mainly for the difference array:
- Difference array
diff
: A fixed-size array of length52
is used, so space complexity isO(1)
, as it doesn't depend on the input size.
Therefore, the overall space complexity of the algorithm is O(1)
.
Learn more about how to find time and space complexity quickly using problem constraints.
Which of the following is a good use case for backtracking?
Recommended Readings
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!