Skip to content

1851. Minimum Interval to Include Each Query (Hard)

Problem

You’re given intervals and queries. For each query q, find the length of the smallest interval that contains q (i.e., start ≤ q ≤ end), or -1 if no such interval exists.

Example

  • intervals = [[1,4],[2,4],[3,6],[4,4]], queries = [2, 3, 4, 5][3, 3, 1, 4]
  • intervals = [[2,3],[2,5],[1,8],[20,25]], queries = [2, 19, 5, 22][2, -1, 4, 4]

LeetCode 1851 · Link · Hard

Try it yourself

idle

Starter code: this editor begins with intentional TODOs. Fill the function, run the embedded tests, then compare your solution with the worked approaches below.

Click Run Python to execute. First run downloads Python (~10 MB, cached after that).

Approach 1: Brute force, per query, scan all intervals

For each query, iterate intervals and track the smallest containing length.

def min_interval(intervals, queries):
result = []
for q in queries: # L1: q iterations
best = float('inf')
for s, e in intervals: # L2: n iterations per query
if s <= q <= e:
best = min(best, e - s + 1)
result.append(best if best != float('inf') else -1)
return result

Each query independently scans every interval. No state shared across queries.

Complexity

  • Time: O(nq)O(n · q).
  • Space: O(q)O(q).

Acceptable only when both n and q are small.

Approach 2: Offline queries + min-heap (canonical)

Sort queries and intervals by start. Walk queries in order; for each query, push all intervals whose start ≤ query into a min-heap keyed by length. Then pop heap entries whose end < query (they don’t contain it). The heap top is the smallest containing interval.

import heapq
def min_interval(intervals, queries):
intervals.sort(key=lambda x: x[0]) # L1: O(n log n)
sorted_queries = sorted(enumerate(queries), key=lambda p: p[1]) # L2: O(q log q)
result = [0] * len(queries) # L3: O(q)
heap = [] # (length, end) # L4: O(1)
i = 0 # L5: O(1)
for orig_idx, q in sorted_queries: # L6: O(q) outer loop
while i < len(intervals) and intervals[i][0] <= q: # L7: O(1) per check
s, e = intervals[i]
heapq.heappush(heap, (e - s + 1, e)) # L8: O(log n) per push
i += 1
while heap and heap[0][1] < q: # L9: O(1) per check
heapq.heappop(heap) # L10: O(log n) per pop
result[orig_idx] = heap[0][0] if heap else -1 # L11: O(1)
return result

Where the time goes, line by line

Variables: n = len(intervals), q = len(queries).

LinePer-call costTimes executedContribution
L1 (sort intervals)O(nlogn)O(n log n)1O(nlogn)O(n log n)
L2 (sort queries)O(qlogq)O(q log q)1O(qlogq)O(q log q)
L3 (init result)O(q)O(q)1O(q)O(q)
L6 (outer loop)O(1)O(1)qO(q)O(q)
L7, L8 (push intervals)O(logn)O(log n)n totalO(nlogn)O(n log n) ← dominates with L10
L9, L10 (pop stale)O(logn)O(log n)n totalO(nlogn)O(n log n) ← dominates
L11 (read top)O(1)O(1)qO(q)O(q)

Each interval is pushed at most once (L8) and popped at most once (L10). The outer loop sees q queries. Combined: O((n+q)O((n + q) log(n + q)).

Complexity

  • Time: O((n+q)O((n + q) log(n + q)), driven by L1/L2 sorts and L8/L10 heap operations.
  • Space: O(n+q)O(n + q).

Why offline sorting helps

Processing queries out of order turns a “for each query, find the best interval” problem into a stream. Sorting queries by value + sorting intervals by start makes the two monotonic, so a single sweep can answer every query.

Try this approach:

idle
Click Run Python to execute. First run downloads Python (~10 MB, cached after that).

Approach 3: Segment tree / merge sort tree

More code and more flexibility. Used in competitive programming for online variants. The shape: coordinate-compress the endpoints, then for each interval do a range-min update of its length over [start, end]. Each query is a point query for the minimum length covering that coordinate.

def min_interval(intervals, queries):
# Coordinate compression
coords = sorted(set([s for s, _ in intervals]
+ [e for _, e in intervals]
+ list(queries)))
coord_idx = {c: i for i, c in enumerate(coords)}
n = len(coords)
INF = float('inf')
tree = [INF] * (4 * max(n, 1))
def update(node, lo, hi, l, r, val):
if r < lo or hi < l:
return
if l <= lo and hi <= r:
tree[node] = min(tree[node], val)
return
mid = (lo + hi) // 2
update(2*node, lo, mid, l, r, val)
update(2*node+1, mid+1, hi, l, r, val)
def query(node, lo, hi, idx):
if lo == hi:
return tree[node]
mid = (lo + hi) // 2
if idx <= mid:
return min(tree[node], query(2*node, lo, mid, idx))
return min(tree[node], query(2*node+1, mid+1, hi, idx))
for s, e in intervals:
update(1, 0, n - 1, coord_idx[s], coord_idx[e], e - s + 1)
result = []
for q in queries:
ans = query(1, 0, n - 1, coord_idx[q])
result.append(ans if ans != INF else -1)
return result

Skip unless the interviewer asks for O(logn)O(log n) per query online.

Summary

ApproachTimeSpace
Per-query scanO(nq)O(n · q)O(q)O(q)
Offline + heap sweepO((n+q)O((n + q) log (n + q))O(n+q)O(n + q)
Segment treeO((n+q)O((n + q) log n)O(n)O(n)

Offline query processing is a broadly useful pattern whenever you have a batch of queries that can be answered by a single sorted sweep.

Test cases

# Quick smoke tests, paste into a REPL or save as test_1851_minimum_interval.py and run.
# Uses the canonical implementation (Approach 2).
import heapq
def min_interval(intervals, queries):
intervals.sort(key=lambda x: x[0])
sorted_queries = sorted(enumerate(queries), key=lambda p: p[1])
result = [0] * len(queries)
heap = [] # (length, end)
i = 0
for orig_idx, q in sorted_queries:
while i < len(intervals) and intervals[i][0] <= q:
s, e = intervals[i]
heapq.heappush(heap, (e - s + 1, e))
i += 1
while heap and heap[0][1] < q:
heapq.heappop(heap)
result[orig_idx] = heap[0][0] if heap else -1
return result
def _run_tests():
# Example 1 from problem statement
assert min_interval([[1,4],[2,4],[3,6],[4,4]], [2,3,4,5]) == [3,3,1,4]
# Example 2
assert min_interval([[2,3],[2,5],[1,8],[20,25]], [2,19,5,22]) == [2,-1,4,6]
# Query with no matching interval
assert min_interval([[1,3]], [5]) == [-1]
# Single interval, single query inside
assert min_interval([[1,10]], [5]) == [10]
# Multiple queries all answered by same smallest interval
assert min_interval([[1,5],[2,3]], [2,3]) == [2,2]
print("all tests pass")
if __name__ == "__main__":
_run_tests()
  • Difference Arrays, the boundary marking model for range updates and overlap counts.
  • Intervals, the range representation behind starts, ends, overlap, and coverage.