Facebook Pixel

1906. Minimum Absolute Difference Queries

MediumArrayHash Table
Leetcode Link

Problem Description

You are given an integer array nums and a 2D array queries where each query is represented as [li, ri]. For each query, you need to find the minimum absolute difference in the subarray nums[li...ri].

The minimum absolute difference of an array is the smallest value of |a[i] - a[j]| where:

  • 0 <= i < j < array.length
  • a[i] != a[j] (the two elements must be different)
  • If all elements in the array are the same, return -1

For example, in the array [5, 2, 3, 7, 2]:

  • The minimum absolute difference is |2 - 3| = 1
  • Note that even though there are two 2's, we can't use |2 - 2| = 0 because the elements must be different values

Your task is to process each query and return an array ans where ans[i] is the minimum absolute difference for the subarray specified by the i-th query.

The solution uses a prefix sum approach to efficiently track which values (1-100) appear in each subarray range. For each query:

  1. It builds a frequency count of values present in the subarray using prefix sums
  2. It identifies all distinct values that appear in the range
  3. It finds the minimum difference between consecutive distinct values
  4. If only one distinct value exists (all elements are the same), it returns -1

The time complexity is O(m * 100 + n * 100) where m is the length of nums and n is the number of queries, since values are constrained to the range [1, 100].

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that to find the minimum absolute difference between different elements, we need to identify which distinct values exist in a subarray and find the smallest gap between them.

Since we're dealing with multiple queries on different subarrays, a naive approach of sorting each subarray would be inefficient. We need a way to quickly determine which values are present in any given range.

The crucial observation is that the problem constraints limit values to the range [1, 100]. This small range allows us to use a fixed-size frequency tracking approach. Instead of sorting or using complex data structures, we can simply track the presence of each possible value.

To efficiently answer range queries, we can use prefix sums. For each position in the array and each possible value (1 to 100), we maintain a count of how many times that value has appeared up to that position. This creates a 2D prefix sum array where pre_sum[i][j] represents the count of value j in nums[0...i-1].

With this preprocessing, for any query [left, right], we can instantly determine if value j exists in that range by checking if pre_sum[right+1][j] - pre_sum[left][j] > 0.

Once we know which values are present, finding the minimum difference becomes straightforward: we iterate through all possible values (1 to 100) in order, track consecutive values that appear in the range, and calculate their differences. The minimum of these differences is our answer.

This approach trades space for time - we use O(m * 100) space to achieve O(100) time per query, which is constant time given the constraint on values.

Solution Approach

The solution implements a prefix sum approach with the following steps:

1. Building the Prefix Sum Array:

pre_sum = [[0] * 101 for _ in range(m + 1)]

We create a 2D array pre_sum where pre_sum[i][j] stores the count of value j in the first i elements of nums. The array has dimensions (m+1) × 101 where m is the length of nums.

for i in range(1, m + 1):
    for j in range(1, 101):
        t = 1 if nums[i - 1] == j else 0
        pre_sum[i][j] = pre_sum[i - 1][j] + t

For each position i and value j, we check if nums[i-1] == j. If yes, we increment the count from the previous position by 1, otherwise we keep the same count.

2. Processing Each Query:

For each query [left, right]:

left, right = queries[i][0], queries[i][1] + 1

We adjust right to right + 1 to make it inclusive for our prefix sum calculation.

3. Finding Present Values and Minimum Difference:

t = inf
last = -1
for j in range(1, 101):
    if pre_sum[right][j] - pre_sum[left][j] > 0:
        if last != -1:
            t = min(t, j - last)
        last = j
  • We iterate through all possible values from 1 to 100
  • For each value j, we check if it exists in the range using: pre_sum[right][j] - pre_sum[left][j] > 0
  • If the value exists:
    • If we've seen a previous value (last != -1), we calculate the difference j - last and update our minimum
    • We update last to the current value j

4. Handling Edge Cases:

if t == inf:
    t = -1
ans.append(t)

If t remains as inf, it means we found only one distinct value (or none), so we return -1 as specified.

The algorithm efficiently handles range queries in O(100) time per query after O(m × 100) preprocessing, making it suitable for multiple queries on the same array.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with a concrete example:

Input: nums = [4, 2, 3, 2, 4], queries = [[0, 2], [2, 4], [0, 4]]

Step 1: Build Prefix Sum Array

We create a prefix sum array to track occurrences of each value (1-100) up to each position:

Position:     0     1     2     3     4     5
nums:         -    [4]   [2]   [3]   [2]   [4]

Key prefix sum values (showing only values 2, 3, 4 for brevity):

pre_sum[0][2] = 0, pre_sum[0][3] = 0, pre_sum[0][4] = 0
pre_sum[1][2] = 0, pre_sum[1][3] = 0, pre_sum[1][4] = 1
pre_sum[2][2] = 1, pre_sum[2][3] = 0, pre_sum[2][4] = 1
pre_sum[3][2] = 1, pre_sum[3][3] = 1, pre_sum[3][4] = 1
pre_sum[4][2] = 2, pre_sum[4][3] = 1, pre_sum[4][4] = 1
pre_sum[5][2] = 2, pre_sum[5][3] = 1, pre_sum[5][4] = 2

Step 2: Process Query [0, 2] (subarray: [4, 2, 3])

Check which values exist in range [0, 2]:

  • Value 2: pre_sum[3][2] - pre_sum[0][2] = 1 - 0 = 1 ✓ (exists)
  • Value 3: pre_sum[3][3] - pre_sum[0][3] = 1 - 0 = 1 ✓ (exists)
  • Value 4: pre_sum[3][4] - pre_sum[0][4] = 1 - 0 = 1 ✓ (exists)

Find minimum difference between consecutive present values:

  • First present value: 2 (set last = 2)
  • Next present value: 3 → difference = 3 - 2 = 1
  • Next present value: 4 → difference = 4 - 3 = 1
  • Minimum difference = 1

Step 3: Process Query [2, 4] (subarray: [3, 2, 4])

Check which values exist in range [2, 4]:

  • Value 2: pre_sum[5][2] - pre_sum[2][2] = 2 - 1 = 1 ✓ (exists)
  • Value 3: pre_sum[5][3] - pre_sum[2][3] = 1 - 0 = 1 ✓ (exists)
  • Value 4: pre_sum[5][4] - pre_sum[2][4] = 2 - 1 = 1 ✓ (exists)

Find minimum difference:

  • Present values in order: 2, 3, 4
  • Differences: 3 - 2 = 1, 4 - 3 = 1
  • Minimum difference = 1

Step 4: Process Query [0, 4] (subarray: [4, 2, 3, 2, 4])

Check which values exist in range [0, 4]:

  • Value 2: pre_sum[5][2] - pre_sum[0][2] = 2 - 0 = 2 ✓ (exists)
  • Value 3: pre_sum[5][3] - pre_sum[0][3] = 1 - 0 = 1 ✓ (exists)
  • Value 4: pre_sum[5][4] - pre_sum[0][4] = 2 - 0 = 2 ✓ (exists)

Find minimum difference:

  • Present values in order: 2, 3, 4
  • Differences: 3 - 2 = 1, 4 - 3 = 1
  • Minimum difference = 1

Final Answer: [1, 1, 1]

The algorithm efficiently finds that all three queries have a minimum absolute difference of 1, using the prefix sum array to quickly identify which values are present in each subarray range.

Solution Implementation

1class Solution:
2    def minDifference(self, nums: List[int], queries: List[List[int]]) -> List[int]:
3        """
4        Find the minimum absolute difference between any two distinct elements 
5        in the subarray for each query.
6      
7        Args:
8            nums: List of integers (values range from 1 to 100)
9            queries: List of [left, right] index pairs representing subarrays
10      
11        Returns:
12            List of minimum differences for each query, -1 if no valid difference exists
13        """
14        num_elements = len(nums)
15        num_queries = len(queries)
16      
17        # Build prefix sum array for each value from 1 to 100
18        # prefix_count[i][val] = count of 'val' in nums[0:i]
19        MAX_VALUE = 100
20        prefix_count = [[0] * (MAX_VALUE + 1) for _ in range(num_elements + 1)]
21      
22        # Calculate prefix sums for each possible value
23        for i in range(1, num_elements + 1):
24            for value in range(1, MAX_VALUE + 1):
25                # Copy previous counts
26                prefix_count[i][value] = prefix_count[i - 1][value]
27                # Increment if current element equals this value
28                if nums[i - 1] == value:
29                    prefix_count[i][value] += 1
30      
31        # Process each query
32        result = []
33        for query_idx in range(num_queries):
34            left_idx = queries[query_idx][0]
35            right_idx = queries[query_idx][1] + 1  # Convert to exclusive right bound
36          
37            # Find minimum difference between distinct values in the subarray
38            min_diff = float('inf')
39            prev_value = -1
40          
41            # Check which values appear in the subarray
42            for value in range(1, MAX_VALUE + 1):
43                count_in_range = prefix_count[right_idx][value] - prefix_count[left_idx][value]
44              
45                if count_in_range > 0:  # This value exists in the subarray
46                    if prev_value != -1:
47                        # Calculate difference with previous existing value
48                        min_diff = min(min_diff, value - prev_value)
49                    prev_value = value
50          
51            # If no valid difference found (less than 2 distinct values), return -1
52            if min_diff == float('inf'):
53                min_diff = -1
54              
55            result.append(min_diff)
56          
57        return result
58
1class Solution {
2    public int[] minDifference(int[] nums, int[][] queries) {
3        int arrayLength = nums.length;
4        int queryCount = queries.length;
5      
6        // Build prefix sum array for each value from 1 to 100
7        // prefixSum[i][j] represents count of value j in nums[0...i-1]
8        int[][] prefixSum = new int[arrayLength + 1][101];
9      
10        for (int i = 1; i <= arrayLength; i++) {
11            for (int value = 1; value <= 100; value++) {
12                // Check if current element equals the value
13                int increment = (nums[i - 1] == value) ? 1 : 0;
14                // Accumulate count from previous position
15                prefixSum[i][value] = prefixSum[i - 1][value] + increment;
16            }
17        }
18      
19        // Process each query to find minimum difference
20        int[] result = new int[queryCount];
21      
22        for (int queryIndex = 0; queryIndex < queryCount; queryIndex++) {
23            int leftBound = queries[queryIndex][0];
24            int rightBound = queries[queryIndex][1] + 1;  // Convert to exclusive right bound
25          
26            int minDiff = Integer.MAX_VALUE;
27            int previousValue = -1;
28          
29            // Check each possible value from 1 to 100
30            for (int value = 1; value <= 100; value++) {
31                // Check if this value exists in the query range
32                if (prefixSum[rightBound][value] > prefixSum[leftBound][value]) {
33                    // If we have a previous value, calculate difference
34                    if (previousValue != -1) {
35                        minDiff = Math.min(minDiff, value - previousValue);
36                    }
37                    // Update previous value for next iteration
38                    previousValue = value;
39                }
40            }
41          
42            // If no valid difference found, return -1
43            if (minDiff == Integer.MAX_VALUE) {
44                minDiff = -1;
45            }
46          
47            result[queryIndex] = minDiff;
48        }
49      
50        return result;
51    }
52}
53
1class Solution {
2public:
3    vector<int> minDifference(vector<int>& nums, vector<vector<int>>& queries) {
4        int arraySize = nums.size();
5        int queryCount = queries.size();
6      
7        // Create prefix sum array for each value from 1 to 100
8        // prefixSum[i][j] represents count of value j in nums[0...i-1]
9        int prefixSum[arraySize + 1][101];
10      
11        // Initialize prefix sum array with zeros (implicit in stack allocation)
12        // Build prefix sum for each possible value (1 to 100)
13        for (int i = 1; i <= arraySize; ++i) {
14            for (int value = 1; value <= 100; ++value) {
15                // Check if current element equals this value
16                int increment = (nums[i - 1] == value) ? 1 : 0;
17                // Update prefix sum
18                prefixSum[i][value] = prefixSum[i - 1][value] + increment;
19            }
20        }
21      
22        // Process each query to find minimum difference
23        vector<int> result(queryCount);
24      
25        for (int queryIdx = 0; queryIdx < queryCount; ++queryIdx) {
26            // Get query range [left, right], converting to 1-indexed
27            int leftIdx = queries[queryIdx][0];
28            int rightIdx = queries[queryIdx][1] + 1;
29          
30            // Initialize minimum difference to maximum possible value
31            int minDiff = 101;
32            int previousValue = -1;
33          
34            // Check each possible value from 1 to 100
35            for (int value = 1; value <= 100; ++value) {
36                // Check if this value exists in the query range
37                if (prefixSum[rightIdx][value] > prefixSum[leftIdx][value]) {
38                    // If we have a previous value, calculate difference
39                    if (previousValue != -1) {
40                        minDiff = min(minDiff, value - previousValue);
41                    }
42                    // Update previous value for next iteration
43                    previousValue = value;
44                }
45            }
46          
47            // If no valid difference found, return -1
48            if (minDiff == 101) {
49                minDiff = -1;
50            }
51          
52            result[queryIdx] = minDiff;
53        }
54      
55        return result;
56    }
57};
58
1function minDifference(nums: number[], queries: number[][]): number[] {
2    const arrayLength: number = nums.length;
3    const queriesCount: number = queries.length;
4    const maxValue: number = 100; // Maximum possible value in nums array
5  
6    // Build prefix sum array for each distinct value (1 to 100)
7    // prefixCount[i][j] represents count of value j in nums[0...i-1]
8    const prefixCount: number[][] = [];
9    prefixCount.push(new Array(maxValue + 1).fill(0)); // Initialize first row with zeros
10  
11    // Populate prefix count array
12    for (let i = 0; i < arrayLength; i++) {
13        const currentNum: number = nums[i];
14        // Copy previous row's counts
15        prefixCount.push([...prefixCount[i]]);
16        // Increment count for current number
17        prefixCount[i + 1][currentNum]++;
18    }
19  
20    const result: number[] = [];
21  
22    // Process each query
23    for (const [leftIndex, rightIndex] of queries) {
24        let previousValue: number = -1; // Track the last seen value in range
25        let minDiff: number = Infinity; // Track minimum difference between consecutive values
26      
27        // Check each possible value from 1 to maxValue
28        for (let value = 1; value <= maxValue; value++) {
29            // Check if this value exists in the query range [leftIndex, rightIndex]
30            const countInRange: number = prefixCount[rightIndex + 1][value] - prefixCount[leftIndex][value];
31          
32            if (countInRange > 0) {
33                // If we have a previous value, calculate difference
34                if (previousValue !== -1) {
35                    minDiff = Math.min(minDiff, value - previousValue);
36                }
37                previousValue = value;
38            }
39        }
40      
41        // Add result: -1 if no valid difference found, otherwise the minimum difference
42        result.push(minDiff === Infinity ? -1 : minDiff);
43    }
44  
45    return result;
46}
47

Time and Space Complexity

Time Complexity: O(m * 101 + n * 101) which simplifies to O(m + n)

  • Building the prefix sum array requires iterating through all m elements of nums, and for each element, we update 101 values (constant), resulting in O(m * 101) = O(m) time.
  • Processing queries requires iterating through all n queries. For each query, we check all 101 possible values (constant) to find which values exist in the subarray and calculate the minimum difference, taking O(n * 101) = O(n) time.
  • The overall time complexity is O(m + n) where m is the length of nums and n is the number of queries.

Space Complexity: O(m * 101) which simplifies to O(m)

  • The prefix sum array pre_sum has dimensions (m + 1) × 101, requiring O((m + 1) * 101) = O(m) space.
  • The answer array ans stores n results, requiring O(n) space.
  • Since typically m ≥ n in practical scenarios and the prefix sum dominates, the overall space complexity is O(m).
  • Additional variables like left, right, t, and last use constant O(1) space.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect Handling of Same Elements

A critical pitfall is misunderstanding that we need the minimum difference between distinct values, not between different positions of the same value.

Wrong Approach:

# Incorrect: Finding minimum difference between any two elements
min_diff = float('inf')
for i in range(left, right + 1):
    for j in range(i + 1, right + 1):
        min_diff = min(min_diff, abs(nums[i] - nums[j]))

This would incorrectly return 0 for [2, 5, 2] by comparing the two 2's.

Correct Approach: Only compare elements with different values, which the prefix sum solution handles by tracking distinct values present in the range.

2. Off-by-One Errors in Index Handling

The queries use 0-based indexing, but the prefix sum array needs careful index management.

Wrong:

# Forgetting to adjust indices properly
left, right = queries[i][0], queries[i][1]  # Missing +1 for right
count = prefix_count[right][value] - prefix_count[left][value]  # Wrong!

Correct:

left_idx = queries[query_idx][0]
right_idx = queries[query_idx][1] + 1  # Convert to exclusive right bound
count = prefix_count[right_idx][value] - prefix_count[left_idx][value]

3. Memory Inefficiency with Large Value Ranges

If the value range wasn't constrained to [1, 100], using a fixed-size array would be wasteful or impossible.

Problematic for large ranges:

# If values could be up to 10^9, this would use excessive memory
prefix_count = [[0] * (10**9 + 1) for _ in range(n + 1)]  # Memory error!

Solution for unconstrained values:

# Use dictionary-based approach for sparse values
from collections import defaultdict

def minDifference(nums, queries):
    result = []
    for left, right in queries:
        # Collect unique values in subarray
        unique_vals = sorted(set(nums[left:right+1]))
      
        if len(unique_vals) < 2:
            result.append(-1)
        else:
            min_diff = min(unique_vals[i+1] - unique_vals[i] 
                          for i in range(len(unique_vals)-1))
            result.append(min_diff)
    return result

4. Not Handling Edge Cases Properly

Failing to return -1 when all elements in the subarray are identical.

Wrong:

# Forgetting to check if we found at least 2 distinct values
min_diff = float('inf')
for value in range(1, 101):
    # ... find differences ...
result.append(min_diff)  # Would append inf instead of -1

Correct:

if min_diff == float('inf'):
    min_diff = -1  # Properly handle single distinct value case
result.append(min_diff)

5. Inefficient Query Processing Without Preprocessing

Processing each query independently without leveraging preprocessing leads to poor performance.

Inefficient O(n² × q) approach:

def minDifference(nums, queries):
    result = []
    for left, right in queries:
        min_diff = float('inf')
        for i in range(left, right + 1):
            for j in range(i + 1, right + 1):
                if nums[i] != nums[j]:
                    min_diff = min(min_diff, abs(nums[i] - nums[j]))
        result.append(-1 if min_diff == float('inf') else min_diff)
    return result

The prefix sum approach reduces this to O(100) per query after O(n × 100) preprocessing, which is much more efficient for multiple queries.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following is a min heap?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More