Design a data structure to find the frequency of a given value in a given subarray.
The frequency of a value in a subarray is the number of occurrences of that value in the subarray.
Implement the RangeFreqQuery
class:
RangeFreqQuery(int[] arr)
Constructs an instance of the class with the given 0-indexed integer array arr
.int query(int left, int right, int value)
Returns the frequency of value
in the subarray arr[left...right]
.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).
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 whole array.
Constraints:
1 <= arr.length <= 105
1 <= arr[i], value <= 104
0 <= left <= right < arr.length
105
calls will be made to query
This problem asks to design a data structure that efficiently handles frequency queries on subarrays. The optimal solution leverages a combination of hashing and binary search for efficient lookups.
The core idea is to pre-process the input array arr
to create a data structure that allows for quick frequency counting within specified ranges. We achieve this using a hash table (or dictionary in Python) where:
arr
.arr
.Constructor (__init__
or constructor
)
The constructor iterates through arr
. For each element, it adds the element's value as a key to the hash table. If the key already exists, the current index is appended to its associated index list; otherwise, a new entry is created with a list containing the current index.
Query Function (query
)
The query
function takes left
, right
, and value
as input. It performs the following steps:
Check for Existence: It first checks if value
exists as a key in the hash table. If not, the frequency is 0, and the function returns 0.
Binary Search: If value
exists, it retrieves the associated index list (idx
). Crucially, we use binary search (via bisect_left
in Python or Collections.binarySearch
in Java, lower_bound
in C++, sort.SearchInts
in Go, and a custom binary search in TypeScript) to find:
l
: The index of the first element in idx
that is greater than or equal to left
.r
: The index of the first element in idx
that is greater than right
.Frequency Calculation: The difference r - l
represents the number of indices within the range [left, right]
where value
appears. This is the frequency, which is returned.
Time Complexity:
arr
. This is due to the initial iteration through the array (O(N)) plus potentially sorting the index lists (though in practice, many hash table implementations don't explicitly sort lists).Space Complexity: O(N) in the worst case to store the hash table. The total size of all index lists will be at most N.
The code examples provided in the original response are accurate and demonstrate the described approach in several programming languages. The comments within the code further clarify the implementation details. The key aspects are the use of a hash table for efficient value lookups and binary search for efficient range queries within the index lists.