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
At most 105 calls will be made to query
區間內查詢數字的頻率。
請你設計一個資料結構,它能求出給定子陣列內一個給定值的 頻率 。 子陣列中一個值的 頻率 指的是這個子陣列中這個值的出現次數。 請你實現 RangeFreqQuery 類: - RangeFreqQuery(int[] arr) 用下標從 0 開始的整數陣列 arr 構造一個類的例項。 - int query(int left, int right, int value) 返回子陣列 arr[left...right] 中 value 的 頻率 。 一個 子陣列 指的是陣列中一段連續的元素。arr[left...right] 指的是 nums 中包含下標 left 和 right 在內 的中間一段連續元素。
思路
首先我解釋一下題意,尤其是 query 的部分。題目給的是一個陣列,比如這樣的
[12, 33, 4, 56, 22, 2, 34, 33, 22, 12, 34, 56]
然後題目讓我們求的是在某一個區間[left, right]
內,一個目標值的出現次數。比如query(1, 2, 4)
,就是求[33, 4]
這個區間內4
的出現次數;query(0, 11, 33)
,就是求[12, 33, 4, 56, 22, 2, 34, 33, 22, 12, 34, 56]
這個區間內33
的出現次數。
對於一個數字在 input 陣列內出現的下標,我們可以用一個 hashmap 存起來。至於如何高效地找到在某個區間 [left, right]
內到底有幾個數字,則需要用到二分法。
注意程式碼內二分的 helper 函式 helper(left, right, target) 的含義是找在區間 [left, right]
內(實際是在某個數字對應的所有下標)第一個 >= target 的數字的下標。因為我們找的是在一段區間[left, right]
內的目標值,所以我們可以找
- 目標值 target 出現且大於 left 的位置
- 目標值 target 出現且大於 right 的位置
如果目標值 target 出現的位置直接小於 left 或者 直接就大於 right 了,則說明 target 不在區間 [left, right]
內。
複雜度
時間O(n) + O(logn) = O(n)
空間O(n)
程式碼
Java實現
class RangeFreqQuery {
HashMap<Integer, List<Integer>> map;
public RangeFreqQuery(int[] arr) {
map = new HashMap<>();
int n = arr.length;
for (int i = 0; i < n; i++) {
int num = arr[i];
if (!map.containsKey(num)) {
map.put(num, new ArrayList<>());
}
map.get(num).add(i);
}
}
public int query(int left, int right, int value) {
List<Integer> list = map.get(value);
if (list == null) {
return 0;
}
// 找list中第一個 >= left 的下標
int first = helper(list, 0, list.size() - 1, left);
if (list.get(first) > right || list.get(first) < left) {
return 0;
}
// 找list中第一個 >= right 的下標
int second = helper(list, 0, list.size() - 1, right);
if (list.get(second) > right) {
second--;
}
return second - first + 1;
}
private int helper(List<Integer> list, int left, int right, int target) {
while (left < right) {
int mid = left + (right - left) / 2;
if (list.get(mid) < target) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
}
/**
* Your RangeFreqQuery object will be instantiated and called as such:
* RangeFreqQuery obj = new RangeFreqQuery(arr);
* int param_1 = obj.query(left,right,value);
*/