[LeetCode] 2080. Range Frequency Queries

CNoodle發表於2024-11-15

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);
 */

相關文章