查询给定位图所有存储子集的高效数据结构及算法有哪些?
问题背景
设X是互不相同的64位无符号整数std::uint64_t的集合,每个整数被视为代表{1,2,…,64}子集的位图。
核心需求
给定不一定属于X的std::uint64_t类型对象A,列出X中所有满足B是A子集的元素(当A、B被解释为{1,2,…,64}的子集时)。
该判断条件在C++中可直接写为
(A & B) == B
由于A本身不需要属于X,因此该问题和其他同类查询问题不重复。
额外特性:X会随时间动态新增元素(无删除操作),且查询次数远高于X的新增次数。
目前可采用std::set或排序的std::vector存储std::uint64_t类型元素,但性能存在优化空间。
核心问题:有哪些适合存储X的高效数据结构与对应查询算法?该问题属于通用场景问题,暂未找到合适的参考方案。
约束与性能期望
- 基础方案性能参考:若X采用
std::set存储,可选两种查询逻辑:遍历A的所有子集查询,时间复杂度为O(2^m log |X|)(m≤64);或遍历X所有元素查询,时间复杂度为O(|X| log |X|)。 - 性能期望:多数场景下,符合条件的B的数量远小于A的子集总数2m和X的总规模,希望算法运行耗时远低于O(|X|)或O(2m),理想情况接近O(匹配元素数量),最坏情况无法优于O(|X|)。
- 内存约束:允许X存储存在一定内存开销,优先保障查询速度,内存瓶颈远低于时间瓶颈,可接受不超过
std::set存储10倍的内存开销,渐近内存复杂度超过O(|X|)或O(|X| log |X|)的方案不可用。 - 语言约束:C++不是必要限制,核心关注算法与数据结构本身的可行性。
固定集合参考方案:若X为固定集合,可考虑采用哈塞图实现。哈塞图在X每次新增元素时的构建开销较高,若无其他更优方案也可尝试;后续补充其更新开销可能没有预期高,可行性比预估好。
现有可行思路参考
优先选型方案(最终补充)
大概率采用带跳跃距离统计的概率跳表替代std::set,可快速统计区间内的X元素数量,当区间交集元素很少时直接切换为线性搜索,减少搜索区间数量。该方案和顺序统计树能力类似,但跳表的重新实现成本远低于std::set,尤其在不需要支持删除操作的场景下。
区间递归搜索方案
将X存储为按普通数值排序的std::set或std::vector,在不断缩小的区间内执行递归搜索:
- 示例:查询元素A = 10011010,包含最高位的A的子集落在闭区间[10000000, 10011010];包含次高位但不包含最高位的子集落在区间[00010000, 00011010];包含第三位但不包含第二位的子集落在区间[00001000, 00001010];包含第四位但不包含第三位的子集落在区间[00000010, 00000010]。
- 拆分逻辑:对于第一个区间[10000000, 10011010],可基于次高位再拆分两个子搜索区间:[10000000, 10001010]和[10010000, 10011010]。
- 以此类推递归拆分,搜索区间总长度不断缩小,渐近性能远优于遍历全量X的线性搜索。
方案示例
X = {00000010, 00001000, 00110111, 10011100},仅第一层的第一、第三、第四区间和X存在非空交集,最终返回结果为[00000010, 00001000]。
方案已知缺陷
- 若X元素分布较为均匀,该方案的区间拆分存在不均衡问题,每层搜索区间宽度差异较大,上述示例中4个第一层搜索区间的大小分别为27、11、3、1,N更大时间距差异会更明显。
- 若查询集合A有k个有效位,第一层需要构造k个初始搜索区间(单比特搜索),第二层最多2k个,第三层最多4k个,以此类推。
- 经测算,log |X| = O(N)时搜索区间总数量为O(k + 2k + 4k + … + 2^n · k) = O(k²) = O(N²),其中2^n = O(k),每个区间构造耗时为O(N),因此搜索区间构造的时间复杂度约为O(N³)。
- 完整算法复杂度不高于O(N³),由于每个区间可能包含大量元素,通用场景下遍历元素的复杂度下界为O(2^N),但可假设X的元素规模不会超过O(N³)的测算范围。
- 另一个问题是
std::map无法直接查询区间内的元素数量(排序的std::vector支持该能力),因此无法判断何时停止区间拆分,直接遍历区间内剩余的X元素。当然可通过区间大小得到X元素数量的上界,但该上界误差通常较大。
剪枝优化参考:已有类似
std::set且支持快速区间元素计数的实现方式,可适配到类std::map结构中,非常适合该场景的剪枝优化,但C++场景下需要自行重新实现大部分std::map的能力,实现成本较高。
内容的提问来源于stack exchange,提问作者user5428643

