Taro Logo

Minimum Interval to Include Each Query

Hard
Google logo
Google
1 view
Topics:
ArraysBinary Search

You are given a 2D integer array intervals, where intervals[i] = [left<sub>i</sub>, right<sub>i</sub>] describes the i<sup>th</sup> interval starting at left<sub>i</sub> and ending at right<sub>i</sub> (inclusive). The size of an interval is defined as the number of integers it contains, or more formally right<sub>i</sub> - left<sub>i</sub> + 1.

You are also given an integer array queries. The answer to the j<sup>th</sup> query is the size of the smallest interval i such that left<sub>i</sub> <= queries[j] <= right<sub>i</sub>. If no such interval exists, the answer is -1.

Return an array containing the answers to the queries.

Example 1:

Input: intervals = [[1,4],[2,4],[3,6],[4,4]], queries = [2,3,4,5]
Output: [3,3,1,4]
Explanation: The queries are processed as follows:
- Query = 2: The interval [2,4] is the smallest interval containing 2. The answer is 4 - 2 + 1 = 3.
- Query = 3: The interval [2,4] is the smallest interval containing 3. The answer is 4 - 2 + 1 = 3.
- Query = 4: The interval [4,4] is the smallest interval containing 4. The answer is 4 - 4 + 1 = 1.
- Query = 5: The interval [3,6] is the smallest interval containing 5. The answer is 6 - 3 + 1 = 4.

Example 2:

Input: intervals = [[2,3],[2,5],[1,8],[20,25]], queries = [2,19,5,22]
Output: [2,-1,4,6]
Explanation: The queries are processed as follows:
- Query = 2: The interval [2,3] is the smallest interval containing 2. The answer is 3 - 2 + 1 = 2.
- Query = 19: None of the intervals contain 19. The answer is -1.
- Query = 5: The interval [2,5] is the smallest interval containing 5. The answer is 5 - 2 + 1 = 4.
- Query = 22: The interval [20,25] is the smallest interval containing 22. The answer is 25 - 20 + 1 = 6.

How would you efficiently solve this problem?

Solution


Clarifying Questions

When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:

  1. What are the possible ranges for the start, end, and value of each interval, as well as the range for each query value?
  2. Can the intervals overlap? If so, how should overlapping intervals that include a query be handled?
  3. If a query is not included in any interval, what should the return value be?
  4. Are the query values guaranteed to be unique, or might there be duplicate query values?
  5. If multiple intervals have the minimum length and include a query, should I return any of them, or is there a specific criteria for choosing one?

Brute Force Solution

Approach

The brute force method to find the smallest interval that includes each query involves checking every possible interval for each query. For each query, we look at every interval and see if the query falls within that interval. We then find the smallest such interval.

Here's how the algorithm would work step-by-step:

  1. For each query, go through the entire list of intervals one by one.
  2. For each interval, determine if the query number is located inside that interval (meaning it's greater than or equal to the start and less than or equal to the end).
  3. If the query is inside the interval, calculate the length of that interval by subtracting the start from the end and adding one.
  4. After checking all intervals, find the smallest interval length among those intervals which contained the query number.
  5. Return the smallest interval length that was found. If no interval contains the query, return a special value indicating that no such interval exists.

Code Implementation

def min_interval_to_include_each_query_brute_force(intervals, queries):
    results = []

    for query in queries:
        minimum_interval_length = float('inf')

        for interval_start, interval_end in intervals:
            # Check if the query is within the current interval
            if interval_start <= query <= interval_end:

                interval_length = interval_end - interval_start + 1

                # Find the min interval length
                minimum_interval_length = min(minimum_interval_length, interval_length)

        #If no interval contains the query, return -1
        if minimum_interval_length == float('inf'):
            results.append(-1)
        else:
            results.append(minimum_interval_length)

    return results

Big(O) Analysis

Time Complexity
O(n*m)The algorithm iterates through each of the 'm' queries. For each query, it iterates through all 'n' intervals to check if the query falls within the interval. Therefore, for each of the 'm' queries, there are 'n' interval checks, resulting in a total of approximately n*m operations. This gives us a time complexity of O(n*m), where 'n' is the number of intervals and 'm' is the number of queries.
Space Complexity
O(1)The provided brute force algorithm iterates through the intervals and for each query, calculates the interval length if the query falls within the interval. It keeps track of the smallest interval length found so far. This approach uses only a few constant space variables to store the current interval length, the smallest interval length, and loop counters. The space used does not depend on the number of intervals or queries, N. Therefore, the space complexity is O(1).

Optimal Solution

Approach

The best way to solve this puzzle efficiently involves keeping track of intervals and finding the smallest one that answers each question. We use a clever way of sorting and prioritizing to quickly locate the right intervals without checking everything.

Here's how the algorithm would work step-by-step:

  1. First, organize the intervals from smallest to largest based on their size (the difference between their endpoints). Also, keep the questions in their original order, but create a separate sorted order of the questions from smallest to largest.
  2. Go through the questions in sorted order. For each question, look at the smallest interval available. If this interval covers the question, record it as the best answer for that specific question and move to the next question.
  3. If the smallest interval doesn't cover the question, keep looking at intervals in size order. Discard intervals that end before your current question, because they cannot possibly cover the question.
  4. As you examine intervals, keep track of which intervals are valid (meaning they haven't been discarded yet and start before the question you are trying to answer). Then store valid intervals in order by their interval size (smallest to largest). This ensures that you are examining the smallest interval that answers the question.
  5. After answering all the questions (in sorted order), map the results back to the original question order to give the answers in the order that was asked.

Code Implementation

def min_interval(intervals, queries):
    interval_size = lambda interval: interval[1] - interval[0] + 1
    intervals_sorted = sorted(intervals, key=interval_size)
    
    queries_with_index = sorted([(query, index) for index, query in enumerate(queries)])
    result = [0] * len(queries)
    available_intervals = []

    interval_index = 0
    for query, query_index in queries_with_index:
        # Add valid intervals to the heap.
        while interval_index < len(intervals_sorted) and \
              intervals_sorted[interval_index][0] <= query:
            import heapq
            heapq.heappush(available_intervals, \
                           (interval_size(intervals_sorted[interval_index]), \
                            intervals_sorted[interval_index]))
            interval_index += 1
        
        # Remove invalid intervals from the heap.
        while available_intervals and available_intervals[0][1][1] < query:
            import heapq
            heapq.heappop(available_intervals)

        # Assign the smallest valid interval size or -1 if none exists.
        if available_intervals:
            result[query_index] = available_intervals[0][0]
        else:
            result[query_index] = -1

    return result

Big(O) Analysis

Time Complexity
O(n log n)Sorting the intervals by size takes O(n log n) time, where n is the number of intervals. Similarly, sorting the queries takes O(q log q) time, where q is the number of queries. Iterating through the sorted queries and potentially examining each interval in the heap involves a cost that's harder to pin down precisely. However, maintaining the min-heap of valid intervals and inserting/removing intervals contributes logarithmic factors. The overall process approximates O(n log n + q log q), and assuming q <= n, this simplifies to O(n log n).
Space Complexity
O(N)The algorithm creates a sorted copy of the original queries, requiring O(N) space where N is the number of queries. Additionally, it stores the results in a separate array of size N to map the sorted results back to the original order of queries, contributing another O(N) space. The priority queue (or similar data structure implied by "keeping track of valid intervals") for storing valid intervals, in the worst case, could hold all intervals leading to O(N) space. Therefore, the auxiliary space used is dominated by the storage of sorted queries, result array, and potential storage of all intervals, resulting in O(N) space complexity.

Edge Cases

CaseHow to Handle
Empty intervals or queries arrayReturn an empty array since there are no intervals or queries to process, respectively.
Intervals with identical start and end points (single point intervals)The solution should treat these intervals normally, considering their length as zero for length comparison.
Queries array contains duplicate queriesThe solution should handle duplicate queries correctly, potentially requiring storing results using a map to avoid redundant computations.
Large number of intervals or queries (scalability)Ensure the solution uses efficient data structures and algorithms (e.g., binary search, segment tree) to avoid time complexity issues.
Queries outside the range of all intervalsReturn -1 for such queries, as no interval includes them.
Intervals with very large ranges (potential integer overflow)Use appropriate data types (e.g., long) to prevent integer overflow when calculating interval lengths.
Overlapping intervals, needing to find the minimum of overlapping optionsThe solution needs to consider all overlapping intervals and select the one with the minimum length that contains the query.
Intervals are not sortedSort intervals based on their start values to enable efficient binary search for the overlapping intervals.