1. 嵌套区间计数问题概述
嵌套区间计数(Nested Ranges Count)是计算几何和算法设计中的一个经典问题,主要研究如何高效统计一组区间中相互包含关系的数量。给定n个区间[l_i, r_i],我们需要找出每个区间被多少个其他区间完全包含(即满足l_j ≤ l_i且r_i ≤ r_j的区间j的数量)。这个问题在日程调度、基因组比对、IP地址匹配等场景都有重要应用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 问题建模与暴力解法
2.1 基本定义
对于区间集合S = {[l_1,r_1], [l_2,r_2], ..., [l_n,r_n]},定义区间i被区间j包含当且仅当:
- l_j ≤ l_i
- r_i ≤ r_j
2.2 暴力解法实现
最直观的解法是双重循环比较所有区间对:
python复制def nested_ranges_naive(intervals):
n = len(intervals)
counts = [0] * n
for i in range(n):
l_i, r_i = intervals[i]
for j in range(n):
if i == j:
continue
l_j, r_j = intervals[j]
if l_j <= l_i and r_i <= r_j:
counts[i] += 1
return counts
时间复杂度分析:
- 外层循环n次
- 内层循环n次
- 总时间复杂度O(n²)
当n较大时(如n=10^5),这种解法显然不可行。
3. 基于排序与二叉索引树的优化解法
3.1 算法思路
- 将所有区间按左端点升序排序,左端点相同时按右端点降序排序
- 使用二叉索引树(Fenwick Tree)维护右端点的统计信息
- 从右向左处理区间,查询当前区间右端点之前的计数
3.2 具体实现步骤
python复制class FenwickTree:
def __init__(self, size):
self.size = size
self.tree = [0] * (size + 1)
def update(self, index, delta=1):
while index <= self.size:
self.tree[index] += delta
index += index & -index
def query(self, index):
res = 0
while index > 0:
res += self.tree[index]
index -= index & -index
return res
def nested_ranges_optimized(intervals):
# 坐标压缩
sorted_ends = sorted({r for _, r in intervals})
rank = {v: i+1 for i, v in enumerate(sorted_ends)}
# 排序区间
sorted_intervals = sorted(
[(l, r, i) for i, (l, r) in enumerate(intervals)],
key=lambda x: (x[0], -x[1])
)
n = len(intervals)
fenwick = FenwickTree(len(sorted_ends))
counts = [0] * n
# 从右向左处理
for i in range(n-1, -1, -1):
l, r, original_idx = sorted_intervals[i]
compressed_r = rank[r]
counts[original_idx] = fenwick.query(compressed_r)
fenwick.update(compressed_r)
return counts
3.3 复杂度分析
- 排序:O(n log n)
- 坐标压缩:O(n log n)
- Fenwick树操作:每次O(log n),共n次
- 总时间复杂度:O(n log n)
- 空间复杂度:O(n)
4. 算法正确性证明
4.1 排序策略的有效性
将区间按左端点升序、右端点降序排列后,对于任意区间i:
- 在其左侧的区间j满足l_j ≤ l_i
- 由于处理顺序是从右向左,当处理i时,树中只包含右端点≥r_i的区间(因为r_j ≥ r_i的区间j已经被处理过)
4.2 查询操作的正确性
Fenwick树查询r_i位置的前缀和,得到的是右端点≤r_i的区间数量。由于我们按右端点降序处理,这些区间必然满足r_j ≥ r_i,因此查询结果就是包含当前区间的区间数量。
5. 实际应用与变种问题
5.1 实际应用场景
- 日程安排系统:统计每个时间段被多少更长时间段完全包含
- 基因组分析:查找DNA序列中完全包含的重复片段
- 网络流量分析:识别包含关系的IP地址范围
5.2 常见变种问题
- 对称嵌套计数:同时统计每个区间包含多少区间和被多少区间包含
- 加权嵌套计数:每个区间有权重,计算被包含区间的权重和
- 动态嵌套计数:支持区间插入和删除操作
6. 性能优化技巧
6.1 坐标压缩优化
当右端点范围很大时(如1e9),直接建立Fenwick树会消耗过多内存。通过将右端点映射到排序后的秩,可以将空间复杂度从O(max_r)降到O(n)。
6.2 处理重复区间
当存在完全相同的区间时,上述算法会将其视为相互包含。如果需要区分这种情况,可以在排序时加入原始索引作为第三关键字。
6.3 并行化处理
对于超大规模数据(n>1e6),可以将区间分块排序后并行处理,最后合并结果。
7. 测试用例设计
7.1 基础测试用例
python复制intervals = [(1,5), (2,6), (3,4), (7,8)]
# 预期输出: [1,1,2,0]
7.2 边界测试用例
- 空区间列表:[] → []
- 单区间:[(1,2)] → [0]
- 完全相同的区间:[(1,2),(1,2)] → [1,1]
7.3 性能测试用例
生成10^5个随机区间,验证算法在1秒内完成计算。
8. 与其他区间问题的对比
8.1 与区间重叠问题的区别
区间重叠统计的是有交集的区间数量,而嵌套区间要求完全包含关系。前者通常使用扫描线算法,后者更适合用排序+Fenwick树的方法。
8.2 与区间点覆盖问题的关系
区间点覆盖是选择最少的点使得每个区间至少包含一个点,而嵌套区间计数关注的是区间之间的包含关系。两者可以结合解决更复杂的问题。
9. 扩展阅读与参考资料
- 《算法导论》中关于区间树和几何搜索的章节
- Fenwick树的原始论文:Peter M. Fenwick, "A New Data Structure for Cumulative Frequency Tables", 1994
- 计算几何中的平面扫描技术
- 线段树在区间问题中的应用
10. 实际编码注意事项
- 在实现Fenwick树时,注意索引从1开始的设计
- 处理大型数据集时,使用生成器而非列表保存中间结果
- 在工业级实现中,考虑添加输入验证和异常处理
- 对于C++实现,可以使用STL的sort和lower_bound简化代码
提示:在竞赛编程中,这个问题常见于Codeforces和AtCoder的D/E难度题目,通常需要将标准解法优化到极致才能通过所有测试用例。
