medium Range Frequency Queries
Problem Statement
You are given an integer array nums[] and a list of queries queries[], where each query is defined as queries[i] = [lefti, righti, valuei]. For each query, determine the frequency of valuei in the subarray of nums that starts at index lefti and ends at index righti for each query.
Return an array answer[] where answer[i] is the result for the ith query.
Examples
-
Example 1:
- Input: nums =
[2, 1, 2, 3, 2, 1, 3], queries =[[0, 4, 2], [2, 6, 3], [1, 5, 1]] - Expected Output:
[3, 2, 2] - Justification:
- In the first query, 2 appears 3 times in the subarray [2, 1, 2, 3, 2].
- In the second query, 3 appears 2 times in the subarray [2, 3, 2, 1, 3].
- In the third query, 1 appears 2 time in the subarray [1, 2, 3, 2, 1].
- Input: nums =
-
Example 2:
- Input: nums =
[4, 4, 4, 2, 4, 2, 2], queries =[[0, 2, 4], [3, 6, 2], [1, 4, 4]] - Expected Output:
[3, 3, 3] - Justification:
- In the first query, 4 appears 3 times in the subarray [4, 4, 4].
- In the second query, 2 appears 3 times in the subarray [2, 4, 2, 2].
- In the third query, 4 appears 3 times in the subarray [4, 4, 2, 4].
- Input: nums =
-
Example 3:
- Input: nums =
[5, 3, 5, 5, 3, 3, 5, 3, 5], queries =[[0, 8, 5], [1, 5, 3], [2, 7, 5]] - Expected Output:
[5, 3, 3] - Justification:
- In the first query, 5 appears 5 times in the subarray [5, 3, 5, 5, 3, 3, 5, 3, 5].
- In the second query, 3 appears 3 times in the subarray [3, 5, 5, 3, 3].
- In the third query, 5 appears 3 times in the subarray [5, 5, 3, 3, 5, 3].
- Input: nums =
Constraints:
- 1 <= arr.length <= 105
- 1 <= arr[i], value <= 104
- 0 <= left <= right < arr.length
Try it yourself
Try solving this question here:
✅ Solution Range Frequency Queries
Problem Statement
You are given an integer array nums[] and a list of queries queries[], where each query is defined as queries[i] = [lefti, righti, valuei]. For each query, determine the frequency of valuei in the subarray of nums that starts at index lefti and ends at index righti for each query.
Return an array answer[] where answer[i] is the result for the ith query.
Examples
-
Example 1:
- Input: nums =
[2, 1, 2, 3, 2, 1, 3], queries =[[0, 4, 2], [2, 6, 3], [1, 5, 1]] - Expected Output:
[3, 2, 2] - Justification:
- In the first query, 2 appears 3 times in the subarray [2, 1, 2, 3, 2].
- In the second query, 3 appears 2 times in the subarray [2, 3, 2, 1, 3].
- In the third query, 1 appears 2 time in the subarray [1, 2, 3, 2, 1].
- Input: nums =
-
Example 2:
- Input: nums =
[4, 4, 4, 2, 4, 2, 2], queries =[[0, 2, 4], [3, 6, 2], [1, 4, 4]] - Expected Output:
[3, 3, 3] - Justification:
- In the first query, 4 appears 3 times in the subarray [4, 4, 4].
- In the second query, 2 appears 3 times in the subarray [2, 4, 2, 2].
- In the third query, 4 appears 3 times in the subarray [4, 4, 2, 4].
- Input: nums =
-
Example 3:
- Input: nums =
[5, 3, 5, 5, 3, 3, 5, 3, 5], queries =[[0, 8, 5], [1, 5, 3], [2, 7, 5]] - Expected Output:
[5, 3, 3] - Justification:
- In the first query, 5 appears 5 times in the subarray [5, 3, 5, 5, 3, 3, 5, 3, 5].
- In the second query, 3 appears 3 times in the subarray [3, 5, 5, 3, 3].
- In the third query, 5 appears 3 times in the subarray [5, 5, 3, 3, 5, 3].
- Input: nums =
Constraints:
- 1 <= arr.length <= 105
- 1 <= arr[i], value <= 104
- 0 <= left <= right < arr.length
Solution
To solve this problem efficiently, we can use Mo's algorithm, which is well-suited for answering range queries on static arrays. Mo's algorithm sorts queries in a way that minimizes the movement of the left and right pointers, thereby reducing the number of operations needed to adjust the frequency counts. The algorithm processes the queries in a specific order, based on their left and right bounds, and maintains a frequency array that keeps track of the occurrence of each element in the current subarray.
This approach is effective because it reduces the complexity of each query from potentially linear time to logarithmic time. By processing the queries in this way, we minimize the number of elements that need to be added or removed from the subarray, making the solution both time and space-efficient.
Step-by-Step Algorithm
-
Initialization:
- Start by receiving the input array
numsand the list of queriesqueries. - Determine the length of the
numsarray, denoted asn. - Initialize a frequency array
frequency[]with a size 100001 to accommodate the range of values innums.
- Start by receiving the input array
-
Query Preparation:
- For each query in
queries, create an object (or structure) that contains the left indexleft, the right indexright, the value to searchvalue, and the indexiof the query in the original list. - Store all these query objects in an array
queryObjects[].
- For each query in
-
Sorting Queries:
- Sort the
queryObjects[]array using the following criteria:- Primary Criterion: Sort by the block number of the
leftindex. The block number is calculated by dividing theleftindex by the square root ofn(the length ofnums). This helps in minimizing the movement of the left pointer during query processing. - Secondary Criterion: For queries that belong to the same block, sort by the
rightindex. This ensures that the right pointer mostly moves in a single direction, further reducing the computational complexity.
- Primary Criterion: Sort by the block number of the
- Sort the
-
Query Processing:
- Initialize two pointers
currentLeftandcurrentRight, both set to 0. These pointers will represent the current subarray that is being processed. - Initialize an array
result[]to store the result of each query, with the same length asqueries.
- Initialize two pointers
-
Processing Each Query:
- For each query in the sorted
queryObjects[]:- Adjust Right Pointer:
- Expansion: While
currentRightis less than or equal toquery.right, increasefrequency[nums[currentRight]]by 1, and then incrementcurrentRight. This step adds elements to the current subarray and updates their frequency. - Shrinkage: While
currentRightis greater thanquery.right + 1, decrementcurrentRightand decreasefrequency[nums[currentRight]]by 1. This step removes elements from the current subarray that are no longer part of the required range.
- Expansion: While
- Adjust Left Pointer:
- Expansion: While
currentLeftis less thanquery.left, decreasefrequency[nums[currentLeft]]by 1, and then incrementcurrentLeft. This step removes elements from the left side of the subarray that are no longer part of the required range. - Shrinkage: While
currentLeftis greater thanquery.left, decrementcurrentLeftand increasefrequency[nums[currentLeft]]by 1. This step adds elements to the left side of the subarray.
- Expansion: While
- Record the Result:
- The frequency of
query.valuein the current subarray is now stored infrequency[query.value]. Assign this value toresult[query.index].
- The frequency of
- Adjust Right Pointer:
- For each query in the sorted
-
Return the Result:
- After all queries have been processed, return the
result[]array, which contains the frequency counts for each query.
- After all queries have been processed, return the
Algorithm Walkthrough
Given:
nums = [2, 1, 2, 3, 2, 1, 3]queries = [[0, 4, 2], [2, 6, 3], [1, 5, 1]]
Step 1: Initialization
n = 7(length ofnums)frequency[] = [0] * 100001
Step 2: Query Preparation
- Create query objects:
query1: left=0, right=4, value=2, index=0query2: left=2, right=6, value=3, index=1query3: left=1, right=5, value=1, index=2
- Store them in
queryObjects[].
Step 3: Sorting Queries
- Block size
sqrt(n) = sqrt(7) ≈ 2.65. We round down to 2 for block calculations. - Sort queries:
- All queries fall in block 0 or block 1.
- After sorting by the block and
rightindex, the order is:query1 (block 0)query3 (block 0)query2 (block 1)
Step 4: Query Processing Initialization
currentLeft = 0currentRight = 0result[] = [0, 0, 0]
Step 5: Processing Each Query
Processing query1: left=0, right=4, value=2
- Adjust Right Pointer:
- Expand
currentRight:frequency[2] = 1atcurrentRight=0frequency[1] = 1atcurrentRight=1frequency[2] = 2atcurrentRight=2frequency[3] = 1atcurrentRight=3frequency[2] = 3atcurrentRight=4
- Expand
- The subarray is
[2, 1, 2, 3, 2]. - Adjust Left Pointer:
- No adjustments needed,
currentLeftis already atquery.left.
- No adjustments needed,
- Store the Result:
result[0] = frequency[2] = 3
Processing query3: left=1, right=5, value=1
- Adjust Right Pointer:
- Expand
currentRight:frequency[1] = 2atcurrentRight=5
- Expand
- The subarray is
[1, 2, 3, 2, 1]. - Adjust Left Pointer:
- Expand
currentLeft:- Decrease
frequency[2]to 2.currentLeft=1
- Decrease
- Expand
- The subarray is now
[1, 2, 3, 2, 1]fromcurrentLeft=1tocurrentRight=5. - Store the Result:
result[2] = frequency[1] = 2
Processing query2: left=2, right=6, value=3
- Adjust Right Pointer:
- Expand
currentRight:frequency[3] = 2atcurrentRight=6
- Expand
- The subarray is
[2, 3, 2, 1, 3]. - Adjust Left Pointer:
- Expand
currentLeft:- Decrease
frequency[1]to 1.currentLeft=2
- Decrease
- Expand
- The subarray is
[2, 3, 2, 1, 3]fromcurrentLeft=2tocurrentRight=6. - Store the Result:
result[1] = frequency[3] = 2
Step 6: Return the Result
- The final
result[] = [3, 2, 2].
Code
import java.util.Arrays;
class Solution {
private int[] nums;
private int[] frequency;
public int[] rangeFrequencyQueries(int[] nums, int[][] queries) {
this.nums = nums;
int n = nums.length;
frequency = new int[100001]; // assuming nums[i] is <= 100000
Query[] queryObjects = new Query[queries.length];
// Prepare the queries for Mo's algorithm
for (int i = 0; i < queries.length; i++) {
queryObjects[i] = new Query(queries[i][0], queries[i][1], queries[i][2], i);
}
// Sort the queries
Arrays.sort(queryObjects, (q1, q2) -> {
int block1 = q1.left / (int) Math.sqrt(n);
int block2 = q2.left / (int) Math.sqrt(n);
if (block1 != block2) {
return block1 - block2;
}
return q1.right - q2.right;
});
int[] result = new int[queries.length];
int currentLeft = 0, currentRight = 0;
for (Query query : queryObjects) {
// Adjust right boundary
while (currentRight <= query.right) frequency[nums[currentRight++]]++;
while (currentRight > query.right + 1) frequency[nums[--currentRight]]--;
// Adjust left boundary
while (currentLeft < query.left) frequency[nums[currentLeft++]]--;
while (currentLeft > query.left) frequency[nums[--currentLeft]]++;
// Store the result for this query
result[query.index] = frequency[query.value];
}
return result;
}
private class Query {
int left, right, value, index;
Query(int left, int right, int value, int index) {
this.left = left;
this.right = right;
this.value = value;
this.index = index;
}
}
// Main method for testing the examples
public static void main(String[] args) {
Solution solution = new Solution();
// Example 1
int[] nums1 = {2, 1, 2, 3, 2, 1, 3};
int[][] queries1 = {{0, 4, 2}, {2, 6, 3}, {1, 5, 1}};
int[] result1 = solution.rangeFrequencyQueries(nums1, queries1);
System.out.println(Arrays.toString(result1)); // Output: [3, 2, 2]
// Example 2
int[] nums2 = {4, 4, 4, 2, 4, 2, 2};
int[][] queries2 = {{0, 2, 4}, {3, 6, 2}, {1, 4, 4}};
int[] result2 = solution.rangeFrequencyQueries(nums2, queries2);
System.out.println(Arrays.toString(result2)); // Output: [3, 3, 3]
// Example 3
int[] nums3 = {5, 3, 5, 5, 3, 3, 5, 3, 5};
int[][] queries3 = {{0, 8, 5}, {1, 5, 3}, {2, 7, 5}};
int[] result3 = solution.rangeFrequencyQueries(nums3, queries3);
System.out.println(Arrays.toString(result3)); // Output: [5, 3, 3]
}
}
Complexity Analysis
Time Complexity
The overall time complexity is
Space Complexity
- The primary space usage is the
freqarray, which is of size, i.e., in this case. Additionally, we use space for storing the results (O(q)), and the queries themselves, which is .
Thus, the space complexity is
🤖 Don't fully get this? Learn it with Claude
Stuck on Range Frequency Queries? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Progressively stronger hints — you still solve it.
I'm working on the problem **Range Frequency Queries** (DSA). Give me a HINT LADDER: start with the tiniest nudge, then wait. Only reveal the next, stronger hint when I ask. Do NOT show the full solution unless I type 'show solution'. Keep me doing the thinking. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
See the technique, not just code.
Explain the optimal approach to **Range Frequency Queries** with a VISUAL walkthrough: trace it on a small concrete example using ASCII art / a step-by-step diagram, narrate what changes each step, then give time & space complexity with a one-line derivation. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Catch bugs, edge cases, sub-optimality.
I'll paste my solution to **Range Frequency Queries**. Review it for correctness, missed edge cases, and time/space complexity, then coach me toward the optimal — don't just rewrite it. Ask me to paste my code now. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Lock in recognition with look-alikes.
Give me 2 problems that use the SAME underlying pattern as **Range Frequency Queries**. For each, let me attempt first, then review my answer and name the trigger signal that reveals the pattern. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.