2080. 区间内查询数字的频率

利用预处理与二分查找实现区间频率查询 — RangeFreqQuery 详解

题目描述

给定一个整数数组 arr,设计一个数据结构 RangeFreqQuery,支持如下操作:

  • 构造函数RangeFreqQuery(int[] arr):用数组 arr 初始化对象。
  • 查询函数query(int left, int right, int value):返回子数组 arr[left...right] 中元素 value 的出现次数(频率)。

子数组指的是数组中一段连续的元素。arr[left...right] 表示包含下标 leftright 的区间。


示例

RangeFreqQuery rangeFreqQuery = new RangeFreqQuery([12, 33, 4, 56, 22, 2, 34, 33, 22, 12, 34, 56]);
rangeFreqQuery.query(1, 2, 4);  # 返回 1 ,因为子数组 [33, 4] 中 4 出现一次
rangeFreqQuery.query(0, 11, 33); # 返回 2 ,因为整个数组中 33 出现两次

解题分析

此问题的关键点在于,如何高效地支持大量区间查询。

朴素方法

  • 对每次查询,遍历区间 arr[left...right] 统计 value 出现次数。
  • 时间复杂度为 O(n),如果查询次数很多(如10^5次),效率不够高。

高效方案思路

1. 预处理元素出现位置
  • 对每个元素 val,提前记录它在数组中的所有出现索引(下标)。
  • 用一个哈希表(字典)pos,键是元素值,值是该元素所有出现位置的排序列表。

例如,数组 [12, 33, 4, 56, 22, 2, 34, 33, 22, 12, 34, 56]

  • pos[33] = [1, 7]
  • pos[12] = [0, 9]
  • pos[22] = [4, 8]
2. 利用二分查找统计区间出现次数
  • 查询时,已知目标区间 [left, right] 和元素 value
  • pos[value] 的索引列表中:
    • 找第一个位置 >= left 的索引 l (用 bisect_left
    • 找第一个位置 > right 的索引 r (用 bisect_right
  • r - l 即为 value 在该区间出现的次数。

这种方法只需对每次查询做两次二分查找,时间复杂度约为 O(log n)


代码实现(Python)

import bisect
from typing import List

class RangeFreqQuery:

    def __init__(self, arr: List[int]):
        self.pos = {}
        for i, val in enumerate(arr):
            if val not in self.pos:
                self.pos[val] = []
            self.pos[val].append(i)

    def query(self, left: int, right: int, value: int) -> int:
        if value not in self.pos:
            return 0
        positions = self.pos[value]
        l = bisect.bisect_left(positions, left)
        r = bisect.bisect_right(positions, right)
        return r - l

复杂度分析

操作时间复杂度备注
构造函数O(n)遍历数组,记录元素位置
查询函数O(log n)二分查找左右边界
空间复杂度O(n)存储元素所有出现位置

其中,n 是数组长度。


方案比较

方法时间复杂度(查询)备注
朴素遍历O(n)查询次数大时效率极低
线段树/树状数组+哈希O(log n)或更高需要复杂的数据结构支持,编码复杂
预处理 + 二分查找O(log n)实现简单且高效,最优选择

示例说明

假设 arr = [12, 33, 4, 56, 22, 2, 34, 33, 22, 12, 34, 56],执行查询:

  • query(1, 2, 4)
    • pos[4] = [2]
    • [2] 中找第一个 >=1 的位置是0,找第一个 >2 的位置是1,频率 = 1 - 0 = 1。
  • query(0, 11, 33)
    • pos[33] = [1, 7]
    • 找第一个 >=0 的位置是0,找第一个 >11 的位置是2,频率 = 2 - 0 = 2。

总结

本题通过预处理每个元素出现的位置,结合二分查找实现了高效的区间频率查询。该方法简单易懂,且能够支持高达10^5次的查询,适合大规模数据场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值