1. 简介

在本文中,我们将探讨如何计算多个矩形之间的重叠面积。这种问题在多个领域都有实际应用,例如在芯片设计中,某些区域或线路不允许完全重叠,或只能在特定范围内重叠。我们的目标是给定一组矩形,计算它们之间所有重叠部分的总面积。

2. 问题描述

问题看似简单:我们有一组矩形,它们的边都与坐标轴平行(即轴对齐矩形),目标是找出它们之间的重叠区域。注意,我们只关心重叠面积的大小,而不关心重叠区域的具体几何形状。

如下图所示,我们有三个矩形。其中红色和蓝色矩形有重叠区域,面积为 1。

geometry3

3. 简单解法

最直观的方法是两两比较矩形,计算每对之间的重叠面积,然后累加。

3.1 理论思路

每个矩形由两个点定义:左下角和右上角。我们可以将矩形在 x 和 y 轴上的投影看作两个区间。要判断两个矩形是否重叠,只需判断它们在 x 和 y 轴上的投影是否都存在交集。

3.2 实现代码

algorithm CalculateOverlap(l):
    // INPUT
    //    l = a list of rectangles
    // OUTPUT
    //    a = the area of overlap

    n <- the length of l
    a <- 0

    for i <- 0 to n:
        for j <- 0 to n:
            if i != j:
                a <- a + Compare(l[i], l[j])

    return a
algorithm Compare(R1, R2):
    // INPUT
    //    R1, R2 = two rectangles defined by two points A and B
    // OUTPUT
    //    a = the area of Overlap

    X <- 0
    Y <- 0

    if R2.A.x > R1.B.x or R2.B.x < R1.A.x:
        return 0
    else:
        X <- min(R1.B.x, R2.B.x) - max(R1.A.x, R2.A.x) 
    
    if R2.A.y > R1.B.y or R2.B.y < R1.A.y:
        return 0
    else:
        Y <- min(R1.B.y, R2.B.y) - max(R1.A.y, R2.A.y)

    return X * Y

优点:逻辑清晰,实现简单
缺点:时间复杂度为 O(n²),效率低,不适合大规模数据

4. 扫描线法(Sweep Line Algorithm)

为了优化性能,我们可以使用扫描线算法来减少比较次数。该算法通过一条虚拟的垂直线从左向右“扫过”所有矩形的 x 坐标,动态维护当前与扫描线相交的矩形集合。

4.1 算法原理

  • 扫描线从左向右移动,遇到矩形的左边界时将其加入数据结构中。
  • 遇到右边界时将其从数据结构中移除。
  • 每次加入一个矩形时,与当前数据结构中的所有矩形比较,计算重叠面积。

这样我们把二维问题简化为一维问题,减少了不必要的比较。

4.2 示例:添加矩形

如下图所示,我们有四个关键点(事件):分别是两个矩形的左边界和右边界。

geometry2

  • 步骤 A:加入蓝色矩形的左边界 [1,4],记录矩形信息。
  • 步骤 B:加入红色矩形的左边界 [3,7],此时与蓝色矩形进行比较。

4.3 示例:移除矩形

  • 步骤 C:遇到蓝色矩形的右边界,将其从数据结构中移除。
  • 步骤 D:遇到红色矩形的右边界,也移除。

4.4 实现代码

algorithm LineSweep(l):
    // INPUT
    //    l = a list of rectangles
    // OUTPUT
    //    a = the area of their overlap

    a <- 0
    cList <- all x-coordinates from the rectangles
    //each x-coordinate has a reference to its rectangle
    
    n <- the length of cList

    for i <- 0 to n:
        if cList[i] = cList[i].Rectangle.left:
            m <- the length of cList
            for j <- 0 to m:
                a <- a + Compare(l[i], cList[j])
            cList.Add(l[i])
        else:
            cList.Remove(l[i].Rectangle)

    return a

⚠️ 注意:这里我们仍然使用了 Compare() 方法,但通过减少比较次数提升了效率。

5. 优化与复杂度分析

5.1 时间复杂度对比

  • 暴力法:O(n²),因为每对矩形都要比较一次。
  • 扫描线法:仍为 O(n²),因为每加入一个矩形时都要和当前所有矩形比较。

5.2 进一步优化

要真正降低时间复杂度,我们需要优化用于存储和查询矩形的数据结构。可以使用 区间树(Interval Tree)平衡二叉搜索树(BST),这样插入、删除和查找操作的时间复杂度均可降至 O(log n)。

修改扫描线算法中的 cList 为区间树后,整体时间复杂度可优化为 **O(n log n)**。

优化后复杂度:O(n log n)
适用场景:大规模矩形数据集

6. 总结

本文介绍了两种计算矩形重叠面积的方法:

  • 暴力法:简单直接但效率低下,适合小规模数据。
  • 扫描线法:将问题降维,通过动态维护活动矩形集合减少比较次数。
  • 进一步优化:使用区间树将复杂度降至 O(n log n),适合大规模数据场景。

💡 踩坑提醒:在实际项目中,如果矩形数量较大(如上万),一定要避免使用暴力解法,否则很容易导致性能瓶颈。

✅ 推荐优先使用扫描线 + 区间树的方式,兼顾性能与实现难度。


原始标题:How to Find an Area of Overlapping Rectangles

» 下一篇: ASCII 编码简介