LeetCode 记录-850. 矩形面积 II
我的解法
思路
首先,我的想法是,因为正方形可能会有重叠,所以想要通过序列化每个的正方形,来判断是否重复。但是看到(),就打消了这个想法,考虑到这样的空间复杂度和时间复杂度都会特别的高。 第二个想法是,因为看到()。假如设计的算法和 length 呈正比,那么复杂度就会比较低。 想不出什么复杂度比较低的方法,暴力一点就是每每添加一个矩形,就和当前矩形前面的所有矩形对比比较一下,去除掉没有重叠的面积。但这样复杂度应该会比较高。 所以就准备看答案啦。
官方解法 1: 离散化 + 扫描线 + 使用简单数组实时维护
思路
我们先解释扫描线的概念:想象一条竖直的直线从平面的最左端扫到最右端,在扫描的过程中,直线上的一些线段会被给定的矩形覆盖。将这些覆盖的线段长度进行积分,就可以得到矩形的面积之和。每个矩形有一个左边界和一个右边界,在扫描到矩形的左边界时,覆盖的长度可能会增加;在扫描到矩形的右边界时,覆盖的长度可能会减少。如果给定了 n 个矩形,那么覆盖的线段长度最多变化 2n 次,此时我们就可以将两次变化之间的部分合并起来,一起计算:即这一部分矩形的面积,等于覆盖的线段长度,乘以扫描线在水平方向移动过的距离。
因此,我们可以首先将所有矩形的左右边界按照横坐标进行排序,这样就确定了扫描线扫描的顺序。随后我们遍历这些左右边界,一次性地处理掉一批横坐标相同的左右边界,对应地增加或者减少覆盖的长度。在这之后,下一个未遍历到的坐右边界的横坐标,减去这一批左右边界的横坐标,就是扫描线在水平方向移动过的距离。
那么我们如何维护「覆盖的线段长度」呢?这里同样可以使用到离散化的技巧(扫描线就是一种离散化的技巧,将大范围的连续的坐标转化成 2n 个离散的坐标)。由于矩形的上下边界也只有 2n 个,它们会将 y 轴分成 2n+1 个部分,中间的 2n−1 个部分均为线段,会被矩形覆盖到(最外侧的 2 个部分为射线,不会被矩形覆盖到),并且每一个线段要么完全被覆盖,要么完全不被覆盖。因此我们可以使用两个长度为 2n−1 的数组 seg 和 length,其中 seg[i] 表示第 i 个线段被矩形覆盖的次数,length[i] 表示第 i 个线段的长度。当扫描线遇到一个左边界时,我们就将左边界覆盖到的线段对应的 seg[i] 全部加 1;遇到一个右边界时,我们就将右边界覆盖到的线段对应的 seg[i] 全部减 1。在处理掉一批横坐标相同的左右边界后,seg[i] 如果大于 0,说明它被覆盖,我们累加所有的 length[i],即可得到「覆盖的线段长度」。
总体思路和代码看了一下,大致理解了思路,对现在的我来说蛮困难,所以先不看方法 2 了。
代码
class Solution:
def rectangleArea(self, rectangles: List[List[int]]) -> int:
hbound = set()
for rect in rectangles:
# 下边界
hbound.add(rect[1])
# 上边界
hbound.add(rect[3])
hbound = sorted(hbound)
m = len(hbound)
# 「思路与算法部分」的 length 数组并不需要显式地存储下来
# length[i] 可以通过 hbound[i+1] - hbound[i] 得到
seg = [0] * (m - 1)
sweep = list()
for i, rect in enumerate(rectangles):
# 左边界
sweep.append((rect[0], i, 1))
# 右边界
sweep.append((rect[2], i, -1))
sweep.sort()
ans = i = 0
while i < len(sweep):
j = i
while j + 1 < len(sweep) and sweep[i][0] == sweep[j + 1][0]:
j += 1
if j + 1 == len(sweep):
break
# 一次性地处理掉一批横坐标相同的左右边界
for k in range(i, j + 1):
_, idx, diff = sweep[k]
left, right = rectangles[idx][1], rectangles[idx][3]
for x in range(m - 1):
if left <= hbound[x] and hbound[x + 1] <= right:
seg[x] += diff
cover = 0
for k in range(m - 1):
if seg[k] > 0:
cover += (hbound[k + 1] - hbound[k])
ans += cover * (sweep[j + 1][0] - sweep[j][0])
i = j + 1
return ans % (10**9 + 7)
复杂度分析
时间复杂度
O(n^2),其中 n 是矩形的个数。
空间复杂度
O(n),即为扫描线需要使用的空间。