Range Frequency Queries

medium binary search hash map design

Problem

Design a class RangeFreqQuery built from an integer array arr. It must answer many calls of the form query(left, right, value), where each call returns how many times value appears in the subarray arr[left..right] (both ends inclusive). The array never changes, so the work should be done up front and each query should be fast.

Inputarr = [12, 33, 4, 56, 22, 2, 34, 33, 22, 12], query(0, 9, 33)
Output2
The value 33 sits at indices 1 and 7, and both fall inside the range [0, 9], so the answer is 2.

from bisect import bisect_left, bisect_right

class RangeFreqQuery:
    def __init__(self, arr):
        self.pos = {}
        for i, x in enumerate(arr):
            self.pos.setdefault(x, []).append(i)

    def query(self, left, right, value):
        idx = self.pos.get(value)
        if not idx:
            return 0
        lo = bisect_left(idx, left)
        hi = bisect_right(idx, right)
        return hi - lo
// first index in sorted a with a[i] >= target
function lowerBound(a, target) {
  let lo = 0, hi = a.length;
  while (lo < hi) { const mid = (lo + hi) >> 1; if (a[mid] < target) lo = mid + 1; else hi = mid; }
  return lo;
}
// first index in sorted a with a[i] > target
function upperBound(a, target) {
  let lo = 0, hi = a.length;
  while (lo < hi) { const mid = (lo + hi) >> 1; if (a[mid] <= target) lo = mid + 1; else hi = mid; }
  return lo;
}

class RangeFreqQuery {
  constructor(arr) {
    this.pos = new Map();
    for (let i = 0; i < arr.length; i++) {
      if (!this.pos.has(arr[i])) this.pos.set(arr[i], []);
      this.pos.get(arr[i]).push(i);
    }
  }
  query(left, right, value) {
    const idx = this.pos.get(value) || [];
    const lo = lowerBound(idx, left);
    const hi = upperBound(idx, right);
    return hi - lo;
  }
}
class RangeFreqQuery {
    Map<Integer, List<Integer>> pos = new HashMap<>();
    public RangeFreqQuery(int[] arr) {
        for (int i = 0; i < arr.length; i++)
            pos.computeIfAbsent(arr[i], k -> new ArrayList<>()).add(i);
    }
    public int query(int left, int right, int value) {
        List<Integer> idx = pos.getOrDefault(value, new ArrayList<>());
        int lo = lowerBound(idx, left);
        int hi = upperBound(idx, right);
        return hi - lo;
    }
    // first index in sorted a with a.get(i) >= target
    private int lowerBound(List<Integer> a, int target) {
        int lo = 0, hi = a.size();
        while (lo < hi) { int mid = (lo + hi) >>> 1; if (a.get(mid) < target) lo = mid + 1; else hi = mid; }
        return lo;
    }
    // first index in sorted a with a.get(i) > target
    private int upperBound(List<Integer> a, int target) {
        int lo = 0, hi = a.size();
        while (lo < hi) { int mid = (lo + hi) >>> 1; if (a.get(mid) <= target) lo = mid + 1; else hi = mid; }
        return lo;
    }
}
class RangeFreqQuery {
    unordered_map<int, vector<int>> pos;
public:
    RangeFreqQuery(vector<int>& arr) {
        for (int i = 0; i < (int)arr.size(); i++)
            pos[arr[i]].push_back(i);
    }
    int query(int left, int right, int value) {
        auto& idx = pos[value];
        int lo = lower_bound(idx.begin(), idx.end(), left) - idx.begin();
        int hi = upper_bound(idx.begin(), idx.end(), right) - idx.begin();
        return hi - lo;
    }
};
Time: O(n) to build, O(log k) per query Space: O(n)