🧠
Binary Search + Preprocessing Intervals by Length
💡 This approach uses binary search on interval lengths combined with interval coverage checks to find the minimum interval length for each query. It is more advanced and useful when queries are large and intervals can be preprocessed.
Intuition
Sort intervals by length. For each query, binary search on interval lengths to find the smallest length interval that covers the query by checking coverage with prefix sums or segment trees.
Algorithm
- Sort intervals by their length.
- Build a data structure (like segment tree or prefix sums) to quickly check if any interval of a given length covers a query.
- For each query, binary search over interval lengths:
- Check if any interval of that length covers the query using the data structure.
- Return the smallest length found or -1 if none.
💡 This algorithm is more complex but reduces query time by trading off preprocessing and binary search.
from typing import List
import bisect
def min_interval_binary_search(intervals: List[List[int]], queries: List[int]) -> List[int]:
intervals.sort(key=lambda x: x[1] - x[0] + 1)
lengths = [end - start + 1 for start, end in intervals]
starts = [start for start, end in intervals]
ends = [end for start, end in intervals]
def covers(query, length):
# Check if any interval with length <= length covers query
# Using binary search on intervals sorted by length
idx = bisect.bisect_right(lengths, length)
for i in range(idx):
if starts[i] <= query <= ends[i]:
return True
return False
res = []
max_len = lengths[-1] if lengths else 0
for q in queries:
left, right = 1, max_len
ans = -1
while left <= right:
mid = (left + right) // 2
if covers(q, mid):
ans = mid
right = mid - 1
else:
left = mid + 1
res.append(ans)
return res
# Driver code
if __name__ == '__main__':
intervals = [[1,4],[2,4],[3,6],[4,4]]
queries = [2,3,4,5]
print(min_interval_binary_search(intervals, queries)) # Output: [3,3,1,4]
Line Notes
intervals.sort(key=lambda x: x[1] - x[0] + 1)Sort intervals by their length to enable binary search
def covers(query, length):Helper function to check if any interval of length <= given length covers query
idx = bisect.bisect_right(lengths, length)Find index of intervals with length <= current mid
while left <= right:Binary search over possible interval lengths to find minimum covering length
import java.util.*;
public class MinIntervalBinarySearch {
public static int[] minInterval(int[][] intervals, int[] queries) {
Arrays.sort(intervals, Comparator.comparingInt(a -> a[1] - a[0] + 1));
int n = intervals.length;
int[] lengths = new int[n];
int[] starts = new int[n];
int[] ends = new int[n];
for (int i = 0; i < n; i++) {
lengths[i] = intervals[i][1] - intervals[i][0] + 1;
starts[i] = intervals[i][0];
ends[i] = intervals[i][1];
}
int maxLen = n > 0 ? lengths[n - 1] : 0;
int[] res = new int[queries.length];
for (int i = 0; i < queries.length; i++) {
int q = queries[i];
int left = 1, right = maxLen, ans = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (covers(q, mid, lengths, starts, ends)) {
ans = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
res[i] = ans;
}
return res;
}
private static boolean covers(int query, int length, int[] lengths, int[] starts, int[] ends) {
int idx = Arrays.binarySearch(lengths, length);
if (idx < 0) idx = -idx - 1;
for (int i = 0; i < idx; i++) {
if (starts[i] <= query && query <= ends[i]) return true;
}
return false;
}
public static void main(String[] args) {
int[][] intervals = {{1,4},{2,4},{3,6},{4,4}};
int[] queries = {2,3,4,5};
System.out.println(Arrays.toString(minInterval(intervals, queries)));
}
}
Line Notes
Arrays.sort(intervals, Comparator.comparingInt(a -> a[1] - a[0] + 1));Sort intervals by length for binary search
while (left <= right) {Binary search over interval lengths to find minimum covering length
int idx = Arrays.binarySearch(lengths, length);Find index of intervals with length <= mid
for (int i = 0; i < idx; i++) {Check coverage for intervals with length <= mid
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool covers(int query, int length, const vector<int>& lengths, const vector<int>& starts, const vector<int>& ends) {
int idx = (int)(upper_bound(lengths.begin(), lengths.end(), length) - lengths.begin());
for (int i = 0; i < idx; i++) {
if (starts[i] <= query && query <= ends[i]) return true;
}
return false;
}
vector<int> minIntervalBinarySearch(vector<vector<int>>& intervals, vector<int>& queries) {
sort(intervals.begin(), intervals.end(), [](auto& a, auto& b) {
return (a[1] - a[0]) < (b[1] - b[0]);
});
int n = intervals.size();
vector<int> lengths(n), starts(n), ends(n);
for (int i = 0; i < n; i++) {
lengths[i] = intervals[i][1] - intervals[i][0] + 1;
starts[i] = intervals[i][0];
ends[i] = intervals[i][1];
}
int maxLen = n > 0 ? lengths.back() : 0;
vector<int> res;
for (int q : queries) {
int left = 1, right = maxLen, ans = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (covers(q, mid, lengths, starts, ends)) {
ans = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
res.push_back(ans);
}
return res;
}
int main() {
vector<vector<int>> intervals = {{1,4},{2,4},{3,6},{4,4}};
vector<int> queries = {2,3,4,5};
vector<int> result = minIntervalBinarySearch(intervals, queries);
for (int val : result) cout << val << ' ';
cout << endl;
return 0;
}
Line Notes
sort(intervals.begin(), intervals.end(), [](auto& a, auto& b) {Sort intervals by length for binary search
int idx = (int)(upper_bound(lengths.begin(), lengths.end(), length) - lengths.begin());Find count of intervals with length <= mid
while (left <= right) {Binary search over interval lengths to find minimum covering length
for (int i = 0; i < idx; i++) {Check if any interval with length <= mid covers query
function minIntervalBinarySearch(intervals, queries) {
intervals.sort((a,b) => (a[1]-a[0]) - (b[1]-b[0]));
const lengths = intervals.map(i => i[1] - i[0] + 1);
const starts = intervals.map(i => i[0]);
const ends = intervals.map(i => i[1]);
function covers(query, length) {
let idx = upperBound(lengths, length);
for (let i = 0; i < idx; i++) {
if (starts[i] <= query && query <= ends[i]) return true;
}
return false;
}
function upperBound(arr, target) {
let left = 0, right = arr.length;
while (left < right) {
let mid = Math.floor((left + right) / 2);
if (arr[mid] <= target) left = mid + 1;
else right = mid;
}
return left;
}
const maxLen = lengths.length ? lengths[lengths.length - 1] : 0;
const res = [];
for (const q of queries) {
let left = 1, right = maxLen, ans = -1;
while (left <= right) {
let mid = Math.floor((left + right) / 2);
if (covers(q, mid)) {
ans = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
res.push(ans);
}
return res;
}
// Test
const intervals = [[1,4],[2,4],[3,6],[4,4]];
const queries = [2,3,4,5];
console.log(minIntervalBinarySearch(intervals, queries)); // [3,3,1,4]
Line Notes
intervals.sort((a,b) => (a[1]-a[0]) - (b[1]-b[0]));Sort intervals by length for binary search
function covers(query, length) {Check if any interval with length <= given length covers query
while (left <= right) {Binary search over interval lengths to find minimum covering length
let idx = upperBound(lengths, length);Find number of intervals with length <= mid
TimeO(n log n + m n log n)
SpaceO(n + m)
Sorting intervals is O(n log n). For each query, binary searching over lengths is O(log n), but coverage check is O(n) in worst case, leading to O(m n log n) total.
💡 This approach is slower than the heap method for large inputs but shows a different way to think about the problem.
Interview Verdict: Accepted but less efficient
This method is correct but less practical for large inputs due to coverage checks; useful to show binary search skills.