如何创建支持压缩的有序64位long型集合数据结构?
带压缩的有序Long型集合设计方案
针对你提出的倒排索引倒排列表场景——数值唯一有序、大量连续区间+少量跳跃,同时需要支持动态插入/删除、交并集操作且内存占用最小的需求,放弃单值红黑树,改用区间编码红黑树是最优解,以下是具体实现思路:
核心压缩逻辑:区间编码代替单值存储
不再用红黑树存储单个long值,而是存储连续数值区间,每个区间用(start: long, end: long)表示。比如你给出的示例序列1,2,3,1000,1001,1002,15001,15002,会被压缩为3个区间:(1,3)、(1000,1002)、(15001,15002),仅占用6个long的空间(原序列需8个),连续数值越多,压缩比越高。
动态操作的实现(基于区间红黑树)
红黑树的节点为区间对象,按start字段排序,平衡特性保证插入、删除、查找的时间复杂度为O(log n)(n为区间数量,远小于原序列长度)。
插入操作
- 查找候选区间:找到红黑树中
start小于等于插入值x的最大区间(左邻区间),以及start大于x的最小区间(右邻区间)。 - 去重检查:如果
x落在任一已有区间内(left.start <=x <=left.end),直接返回(数值唯一)。 - 合并左邻区间:若左邻区间存在且
left.end +1 ==x,则将左邻区间更新为(left.start, x)。 - 合并右邻区间:若右邻区间存在且
right.start -1 ==x,则将右邻区间(或合并后的左邻区间)更新为(new_start, right.end)。 - 新增区间:若无法合并左右区间,直接插入新节点
(x,x)。
删除操作
- 定位目标区间:找到包含
x的区间(start <=x <=end)。 - 单值区间处理:若区间
start == end,直接删除该节点。 - 多值区间拆分:
- 若
x == start:将区间更新为(start+1, end)。 - 若
x == end:将区间更新为(start, end-1)。 - 若
x在区间中间:删除原区间,插入两个新区间(start, x-1)和(x+1, end)。
- 若
交并集操作优化
基于区间的特性,交并集操作无需遍历单个数值,直接通过区间的重叠/相邻判断完成,效率远高于单值集合:
交集操作
采用双指针遍历两个区间红黑树:
- 初始化指针
p1(指向集合A的第一个区间)、p2(指向集合B的第一个区间)。 - 循环处理直到任一指针为空:
- 计算重叠区间:
overlap_start = max(p1.start, p2.start),overlap_end = min(p1.end, p2.end)。 - 若
overlap_start <= overlap_end,将该区间加入结果集合。 - 移动
end较小的指针:若p1.end < p2.end,p1移至下一个区间,否则p2移至下一个区间。
- 计算重叠区间:
并集操作
同样用双指针遍历合并:
- 初始化指针
p1、p2,结果集合为空。 - 每次取当前
start更小的区间,将其与结果集合的最后一个区间尝试合并(重叠或last.end +1 == current.start则合并),更新结果集合。 - 重复直到所有区间处理完毕。
小规模序列适配优化
当集合元素极少(比如少于10个),区间编码的额外开销(每个区间2个long)可能超过单值存储的成本。此时可以做分层实现:
- 元素数小于阈值时,用有序数组存储单值,插入/删除时通过二分查找定位,交并集直接遍历数组。
- 元素数超过阈值时,自动转换为区间红黑树结构。
内存开销对比
以10万个连续数值为例:
- 原数组:100000 * 8B = 800KB
- 单值红黑树:100000个节点(每个节点含8B数值+24B左右树结构开销)≈ 3.2MB
- 区间红黑树:仅1个节点(16B数值+24B树结构)≈ 40B,压缩效果极其显著。
内容的提问来源于stack exchange,提问作者namdosan
相关产品推荐
相关产品推荐

