You are given a 2D integer array intervals, where intervals[i] = [left_i, right_i] represents the ith interval starting at left_i and ending at right_i (inclusive).
You are also given an integer array of query points queries. The result of query[j] is the length of the shortest interval i such that left_i <= queries[j] <= right_i. If no such interval exists, the result of this query is -1.
Return an array output where output[j] is the result of query[j].
Note: The length of an interval is calculated as right_i - left_i + 1.
Example 1:
Input: intervals = [[1,3],[2,3],[3,7],[6,6]], queries = [2,3,1,7,6,8]
Output: [2,2,3,5,1,-1]Explanation:
[2,3] is the smallest one containing 2, it's length is 2.[2,3] is the smallest one containing 3, it's length is 2.[1,3] is the smallest one containing 1, it's length is 3.[3,7] is the smallest one containing 7, it's length is 5.[6,6] is the smallest one containing 6, it's length is 1.Constraints:
1 <= intervals.length <= 10001 <= queries.length <= 10001 <= left_i <= right_i <= 100001 <= queries[j] <= 10000
You should aim for a solution as good or better than O(nlogn + mlogm) time and O(n + m) space, where m is the size of the array queries and n is the size of the array intervals.
A brute force approach would be to iterate through each query and, for each query, check all intervals to find the result. This would be an O(m * n) solution. Can you think of a better way? Maybe processing the queries in sorted order could help.
We sort the intervals by start value and process the queries in ascending order. Using a pointer i, we add intervals to a min-heap while their start values are less than or equal to the query, storing their end values and sizes.
The min-heap is ordered by interval size. We remove elements from the heap while the top element’s end value is less than the current query. The result for the query is the top element’s size if the heap is non-empty; otherwise, it is -1.
Before attempting this problem, you should be comfortable with:
For each query value q, we want to find the smallest interval length among all intervals [l, r] that contain q (meaning l <= q <= r).
If no interval contains q, we return -1.
The brute force idea is very direct:
r - l + 1res.q in queries:cur = -1 to represent "no covering interval found yet"[l, r]:l <= q <= r, then the interval covers qlen = r - l + 1cur if:cur == -1 (first valid interval), orlen < cur (found a smaller covering interval)cur to res for this query.res.class Solution:
def minInterval(self, intervals: List[List[int]], queries: List[int]) -> List[int]:
n = len(intervals)
res = []
for q in queries:
cur = -1
for l, r in intervals:
if l <= q <= r:
if cur == -1 or (r - l + 1) < cur:
cur = r - l + 1
res.append(cur)
return resWhere is the length of the array and is the length of the array .
For each query value q, we want the length of the smallest interval [l, r] that contains q (l <= q <= r). If none exists, the answer is -1.
A sweep line approach processes everything (interval starts, interval ends, and queries) in sorted order of time.
While sweeping from left to right, we maintain a data structure of “currently active intervals” (intervals that have started but have not ended yet).
For a query time q, the answer is simply the smallest interval length among the active intervals.
To get that smallest length quickly, we use a min-heap ordered by interval size.
Because intervals can end later, we also need a way to remove expired intervals:
[start, end]:start containing its length and indexend containing its indexq:q containing the query's original position(time, type) so they are processed in time order.sizes storing (interval_length, interval_index) for active intervalsinactive to mark intervals that have endedans initialized with -1(length, idx) into the heapans-1ans in the original query order.class Solution:
def minInterval(self, intervals: List[List[int]], queries: List[int]) -> List[int]:
events = []
# Create events for intervals
for idx, (start, end) in enumerate(intervals):
events.append((start, 0, end - start + 1, idx))
events.append((end, 2, end - start + 1, idx))
# Create events for queries
for i, q in enumerate(queries):
events.append((q, 1, i))
# Sort by time and type (end before query)
events.sort(key=lambda x: (x[0], x[1]))
# Min heap storing [size, index]
sizes = []
ans = [-1] * len(queries)
inactive = [False] * len(intervals)
for time, type, *rest in events:
if type == 0: # Interval start
interval_size, idx = rest
heapq.heappush(sizes, (interval_size, idx))
elif type == 2: #Interval end
idx = rest[1]
inactive[idx] = True
else: # Query
query_idx = rest[0]
while sizes and inactive[sizes[0][1]]:
heapq.heappop(sizes)
if sizes:
ans[query_idx] = sizes[0][0]
return ansWhere is the length of the array and is the length of the array .
For each query q, we want the length of the smallest interval [l, r] such thatl ≤ q ≤ r. If no interval covers q, the answer is -1.
A very efficient way to do this is:
qThe heap is ordered by interval length, so the smallest covering interval is easy to find.
intervals by their start value.queries, but keep their original order for the final answer.minHeap storing (interval_length, interval_end)i = 0 to iterate through intervalsres to store answers for each queryq in sorted order:≤ q:(r - l + 1, r) into the heapi forwardq:qq is -1qclass Solution:
def minInterval(self, intervals: List[List[int]], queries: List[int]) -> List[int]:
intervals.sort()
minHeap = []
res = {}
i = 0
for q in sorted(queries):
while i < len(intervals) and intervals[i][0] <= q:
l, r = intervals[i]
heapq.heappush(minHeap, (r - l + 1, r))
i += 1
while minHeap and minHeap[0][1] < q:
heapq.heappop(minHeap)
res[q] = minHeap[0][0] if minHeap else -1
return [res[q] for q in queries]Where is the length of the array and is the length of the array .
For each query q, we need the length of the smallest interval [l, r] such that l <= q <= r.
If no interval covers q, the answer is -1.
This solution treats the number line as a set of discrete important points (all interval endpoints and all queries).
Then it uses a segment tree that supports:
Since many intervals update large ranges, we use lazy propagation to apply updates efficiently without touching every point in the range.
Because coordinates can be large, we first do coordinate compression:
0..M-1compress[value] -> index[l, r]:[L, R]len = r - l + 1[L, R] with len using:tree[node] = min(tree[node], len)q to compressed index idxidx to get the minimum covering length-1 (no interval covers it)class SegmentTree:
def __init__(self, N):
self.n = N
self.tree = [float('inf')] * (4 * N)
self.lazy = [float('inf')] * (4 * N)
def propagate(self, treeidx, lo, hi):
if self.lazy[treeidx] != float('inf'):
self.tree[treeidx] = min(self.tree[treeidx], self.lazy[treeidx])
if lo != hi:
self.lazy[2 * treeidx + 1] = min(self.lazy[2 * treeidx + 1], self.lazy[treeidx])
self.lazy[2 * treeidx + 2] = min(self.lazy[2 * treeidx + 2], self.lazy[treeidx])
self.lazy[treeidx] = float('inf')
def update(self, treeidx, lo, hi, left, right, val):
self.propagate(treeidx, lo, hi)
if lo > right or hi < left:
return
if lo >= left and hi <= right:
self.lazy[treeidx] = min(self.lazy[treeidx], val)
self.propagate(treeidx, lo, hi)
return
mid = (lo + hi) // 2
self.update(2 * treeidx + 1, lo, mid, left, right, val)
self.update(2 * treeidx + 2, mid + 1, hi, left, right, val)
self.tree[treeidx] = min(self.tree[2 * treeidx + 1], self.tree[2 * treeidx + 2])
def query(self, treeidx, lo, hi, idx):
self.propagate(treeidx, lo, hi)
if lo == hi:
return self.tree[treeidx]
mid = (lo + hi) // 2
if idx <= mid:
return self.query(2 * treeidx + 1, lo, mid, idx)
else:
return self.query(2 * treeidx + 2, mid + 1, hi, idx)
def update_range(self, left, right, val):
self.update(0, 0, self.n - 1, left, right, val)
def query_point(self, idx):
return self.query(0, 0, self.n - 1, idx)
class Solution:
def minInterval(self, intervals: List[List[int]], queries: List[int]) -> List[int]:
points = []
for interval in intervals:
points.append(interval[0])
points.append(interval[1])
for q in queries:
points.append(q)
# Compress the coordinates
points = sorted(set(points))
compress = {points[i]: i for i in range(len(points))}
# Lazy Segment Tree
segTree = SegmentTree(len(points))
for interval in intervals:
start = compress[interval[0]]
end = compress[interval[1]]
length = interval[1] - interval[0] + 1
segTree.update_range(start, end, length)
ans = []
for q in queries:
idx = compress[q]
# query for minSize
res = segTree.query_point(idx)
ans.append(res if res != float('inf') else -1)
return ansWhere is the length of the array , is the length of the array and is the number of unique points.
When sorting queries for efficient processing, the original indices must be preserved. Returning results in sorted query order instead of the original order produces incorrect output. Always pair each query with its original index before sorting.
Intervals that have ended (their right endpoint is less than the current query) must be removed from the heap before answering. Failing to pop expired intervals means the heap may return an interval that does not actually contain the query point.
When multiple events occur at the same coordinate, processing order matters. Interval end events should typically be processed before query events at the same point to ensure intervals ending exactly at the query point are still considered active.
The length of interval [l, r] is r - l + 1, not r - l. This off-by-one error affects which interval is considered smallest and leads to incorrect results when intervals differ by exactly one unit.
When no interval covers a query, the answer must be -1. Forgetting to initialize the result array with -1 or not checking for an empty heap after removing expired intervals causes undefined or incorrect values to be returned.