[Medium] 2080. Range Frequency Queries
Design a data structure that can query the frequency of a given value in a given subarray.
Implement the RangeFreqQuery class:
RangeFreqQuery(int[] arr)Constructs an instance of the class with the given0-indexedinteger arrayarr.int query(int left, int right, int value)Returns the frequency ofvaluein the subarrayarr[left...right](inclusive).
A subarray is a contiguous sequence of elements within an array. arr[left...right] denotes the subarray that contains the elements of nums between indices left and right (inclusive).
Examples
Example 1:
Input
["RangeFreqQuery", "query", "query"]
[[[12, 33, 4, 56, 22, 2, 34, 33, 22, 12, 34, 56]], [1, 2, 4], [0, 11, 33]]
Output
[null, 1, 2]
Explanation
RangeFreqQuery rangeFreqQuery = new RangeFreqQuery([12, 33, 4, 56, 22, 2, 34, 33, 22, 12, 34, 56]);
rangeFreqQuery.query(1, 2, 4); // return 1. The value 4 occurs 1 time in the subarray [33, 4]
rangeFreqQuery.query(0, 11, 33); // return 2. The value 33 occurs 2 times in the entire array.
Constraints
1 <= arr.length <= 10^51 <= arr[i] <= 10^40 <= left <= right < arr.length- At most
10^5calls will be made toquery.
Thinking Process
- Preprocessing is Key: Building index map once allows fast queries
lower_bound: First index>= left(inclusive start)upper_bound: First index> right(exclusive end)- Difference gives count in range
- The search space must shrink monotonically each step.
- Decide which half still satisfies the predicate, discard the other.
- Use
mid = left + (right - left) / 2to avoid overflow.
Common Approaches
Typical techniques for this pattern:
| Approach | Time | Space | Notes |
|---|---|---|---|
| Standard binary search (this problem) | O(log n) | O(1) | Sorted array, left <= right |
| Lower / upper bound | O(log n) | O(1) | First/last position, insert index |
| Binary search on rotated array | O(log n) | O(1) | Identify sorted half, discard other |
| Binary search on answer | O(n log M) | O(1) | Monotonic predicate over search space |
Solution
class RangeFreqQuery:
RangeFreqQuery(list[int> arr) :
for(i = 0 i < (int)len(arr) i += 1) :
freqArray[arr[i]].append(i)
def query(self, left, right, value):
if(not value in freqArray) return 0
list[int> v = freqArray[value]
itLeft = lower_bound(v.begin(), v.end(), left)
itRight = upper_bound(v.begin(), v.end(), right)
return itRight - itLeft
dict[int, list[int>> freqArray
/
Your RangeFreqQuery object will be instantiated and called as such:
RangeFreqQuery obj = new RangeFreqQuery(arr)
param_1 = obj.query(left,right,value)
/
Solution Explanation
Approach: Standard binary search (this problem)
Key idea: 1. Preprocessing is Key: Building index map once allows fast queries
How the code works:
- Preprocessing is Key: Building index map once allows fast queries
lower_bound: First index>= left(inclusive start)upper_bound: First index> right(exclusive end)- Difference gives count in range
- The search space must shrink monotonically each step.
- Decide which half still satisfies the predicate, discard the other.
Common Mistakes
- Value not in array:
query(0, 5, 99)→ return0 - Value not in range:
query(3, 5, 33)when33only at indices[1, 7]→ return0 - Single occurrence:
query(0, 11, 4)→ return1 - All occurrences:
query(0, 11, 33)→ return2 -
Single element range:
query(2, 2, 4)→ return1ifarr[2] == 4 - Wrong binary search bounds: Using
upper_boundfor both ends - Not checking existence: Accessing
freqArray[value]without checking - Index confusion: Mixing 0-indexed and 1-indexed arrays
- Not using sorted indices: Assuming indices are sorted without verification
- Inefficient approach: Linear scan for each query (O(n) per query)
Related Problems
- LC 303: Range Sum Query - Immutable - Range sum queries
- LC 307: Range Sum Query - Mutable - Range sum with updates
- LC 315: Count of Smaller Numbers After Self - Counting in ranges
- LC 327: Count of Range Sum - Range counting
Key Takeaways
- Preprocessing is Key: Building index map once allows fast queries
- Sorted Indices: Since we iterate in order, indices are naturally sorted
- Binary Search: Efficiently find indices in range
[left, right] - lower_bound vs upper_bound:
lower_bound: First index>= left(inclusive start)upper_bound: First index> right(exclusive end)- Difference gives count in range
- Hash Map Lookup: O(1) average case to get indices for a value
References
- LC 2080: Range Frequency Queries on LeetCode
- LeetCode Discuss — LC 2080: Range Frequency Queries
- LeetCode Editorial (may require premium)