Check if All the Integers in a Range Are Covered: Boolean Array Marking
LeetCode 1893. Check if All the Integers in a Range Are Covered gives you a list of integer ranges and asks one simple question: does every integer from left to right appear in at least one of those ranges?
It sounds like it should be a one-liner, and the optimal solution nearly is. But the problem is a great exercise in translating a range-covering question into a concrete data structure operation. Let's walk through it.
The problem
You are given a 2D integer array ranges where each ranges[i] = [start_i, end_i] represents an inclusive interval. You are also given two integers left and right. Return True if every integer in the inclusive range [left, right] is covered by at least one interval in ranges. Otherwise return False.
Example:
- Input:
ranges = [[1,2],[3,4],[5,5]],left = 2,right = 5 - Output:
True - Why: The integers 2, 3, 4, and 5 are all covered. 2 is in [1,2], 3 and 4 are in [3,4], and 5 is in [5,5].
The approach
The key insight is that the input ranges are small (values up to 50), so you can afford to "unpack" every range into individual integers. Create a boolean array (or a set) that tracks which integers have been covered. Then iterate through each range, mark every integer in it as covered, and finally check whether all integers from left to right are marked.
Here is the plan:
- Create a set (or boolean array) called
covered. - For each
[start, end]inranges, add every integer fromstarttoendintocovered. - For each integer from
lefttoright, check if it is incovered. If any integer is missing, returnFalse. - If all are present, return
True.
This works because the constraint says values go up to 50, so the total work is bounded and small.
Visual walkthrough
Step 1: Initialize a boolean array covered[] for values 1 through 6, all False.
No integers are marked as covered yet. The query range [2..5] cells are highlighted in amber.
Step 2: Process range [1, 2]. Mark integers 1 and 2 as covered.
covered[1] = True, covered[2] = True. Two query integers (2) are now covered.
Step 3: Process range [3, 4]. Mark integers 3 and 4 as covered.
covered[3] = True, covered[4] = True. Three of four query integers are now covered.
Step 4: Process range [5, 5]. Mark integer 5 as covered.
covered[5] = True. All query integers [2, 3, 4, 5] are now covered.
Step 5: Check if every integer from left=2 to right=5 is covered.
covered[2]=T, covered[3]=T, covered[4]=T, covered[5]=T. All True, so return True.
The code
def is_covered(ranges: list[list[int]], left: int, right: int) -> bool:
covered = set()
for start, end in ranges:
for i in range(start, end + 1):
covered.add(i)
for i in range(left, right + 1):
if i not in covered:
return False
return True
The first loop walks through every range and adds each integer in that range to the covered set. The second loop checks every integer from left to right. If any integer is missing from the set, you return False immediately. If you make it through the entire query range without finding a gap, you return True.
You can also write this more concisely with all():
def is_covered(ranges: list[list[int]], left: int, right: int) -> bool:
covered = set()
for start, end in ranges:
for i in range(start, end + 1):
covered.add(i)
return all(i in covered for i in range(left, right + 1))
Both versions have identical behavior. The explicit loop version is clearer for interviews, while the all() version is more Pythonic.
Complexity analysis
| Complexity | Why | |
|---|---|---|
| Time | O(n * k + r) | You iterate through each of the n ranges, expanding up to k integers per range, then check r integers in the query range. With the constraint that values are at most 50, this is effectively O(1). |
| Space | O(m) | The set holds at most m distinct integers, where m is bounded by the maximum value (50). |
Because the constraints cap all values at 50, both time and space are bounded by a small constant in practice. This makes the brute-force approach perfectly acceptable here.
The building blocks
1. Range expansion into a set
The act of taking an interval [start, end] and materializing every integer in it into a set or array. This pattern shows up whenever you need to reason about individual elements within a range. You will see it again in problems like "Merge Intervals" (where you compare ranges) and "Missing Ranges" (where you find gaps). The core loop is always the same: iterate from start to end and mark each value.
2. Membership verification over a range
After building your coverage data structure, you check whether every integer in a contiguous range is present. This is a linear scan with an early exit: the moment you find a missing value, you stop. The same "verify all elements satisfy a condition" pattern appears in problems like "Contains Duplicate" (checking uniqueness via a set) and "Valid Sudoku" (checking that rows, columns, and boxes contain no repeats).
Edge cases
- Empty ranges list: If
rangesis empty, no integers are covered. You returnFalseunlessleftis greater thanright(which cannot happen per the constraints). - Single-point query: When
left == right, you only need to check whether one integer is covered by any range. The algorithm handles this naturally. - Overlapping ranges: Ranges like
[1,4]and[3,6]overlap at 3 and 4. The set handles this automatically since adding a value that already exists is a no-op. - Query range extends beyond all ranges: If
leftorrightfalls outside every range in the input, those integers will be missing from the set and you returnFalse.
From understanding to recall
You can read through this solution and understand it in a few minutes. The logic is clear, the code is short. But understanding and recall are two different things. When a harder problem requires you to expand ranges into a set, or verify coverage across a contiguous block of values, you want those patterns to fire automatically.
That is what spaced repetition is for. CodeBricks breaks this problem into its building blocks and drills them at increasing intervals. You type each piece from scratch, reinforcing the muscle memory. After a few review cycles, range expansion and membership verification become second nature, ready to deploy the moment you spot them in a new context.
Related posts
- Data Structure: Hash Maps - The set used here is backed by a hash table. Understanding how hash-based lookups achieve O(1) average time is fundamental to this approach.
- Data Structure: Arrays - The boolean array alternative to a set. Arrays are the simplest way to track coverage when the value range is small and known.