|
| 1 | +### 题目描述 |
| 2 | + |
| 3 | +这是 LeetCode 上的 **[850. 矩形面积 II](https://leetcode.cn/problems/rectangle-area-ii/solution/gong-shui-san-xie-by-ac_oier-9r36/)** ,难度为 **困难**。 |
| 4 | + |
| 5 | +Tag : 「扫描线」 |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +我们给出了一个(轴对齐的)二维矩形列表 `rectangles`。 对于 $rectangle[i] = [x_1, y_1, x_2, y_2]$,其中$(x_1, y_1)$ 是矩形 `i` 左下角的坐标,$ (x_{i1}, y_{i1})$ 是该矩形 左下角 的坐标,$ (x_{i2}, y_{i2})$ 是该矩形 右上角 的坐标。 |
| 10 | + |
| 11 | +计算平面中所有 `rectangles` 所覆盖的 总面积 。任何被两个或多个矩形覆盖的区域应只计算 一次 。 |
| 12 | + |
| 13 | +返回 总面积 。因为答案可能太大,返回 $10^9 + 7$ 的 模 。 |
| 14 | + |
| 15 | +示例 1: |
| 16 | + |
| 17 | +``` |
| 18 | +输入:rectangles = [[0,0,2,2],[1,0,2,3],[1,0,3,1]] |
| 19 | +
|
| 20 | +输出:6 |
| 21 | +
|
| 22 | +解释:如图所示,三个矩形覆盖了总面积为6的区域。 |
| 23 | +从(1,1)到(2,2),绿色矩形和红色矩形重叠。 |
| 24 | +从(1,0)到(2,3),三个矩形都重叠。 |
| 25 | +``` |
| 26 | +示例 2: |
| 27 | +``` |
| 28 | +输入:rectangles = [[0,0,1000000000,1000000000]] |
| 29 | +
|
| 30 | +输出:49 |
| 31 | +
|
| 32 | +解释:答案是 1018 对 (109 + 7) 取模的结果, 即 49 。 |
| 33 | +``` |
| 34 | + |
| 35 | +提示: |
| 36 | +* $1 <= rectangles.length <= 200$ |
| 37 | +* $rectanges[i].length = 4$ |
| 38 | +* $0 <= x_{i1}, y_{i1}, x_{i2}, y_{i2} <= 10^9$ |
| 39 | +* 矩形叠加覆盖后的总面积不会超越 $2^{63} - 1$ ,这意味着可以用一个 `64` 位有符号整数来保存面积结果。 |
| 40 | + |
| 41 | +--- |
| 42 | + |
| 43 | +## 扫描线 |
| 44 | + |
| 45 | +这是一道「扫描线」模板题。 |
| 46 | + |
| 47 | +将所有给定的矩形的左右边对应的 `x` 端点提取出来并排序,每个端点可看作是一条竖直的线段(红色),问题转换为求解「由多条竖直线段分割开」的多个矩形的面积总和(黄色): |
| 48 | + |
| 49 | + |
| 50 | + |
| 51 | +相邻线段之间的宽度为单个矩形的「宽度」(通过 `x` 差值直接算得),问题转换为求该区间内高度的并集(即矩形的高度)。 |
| 52 | + |
| 53 | +由于数据范围只有 $200$,我们可以对给定的所有矩形进行遍历,统计所有对该矩形有贡献的 `y` 值线段(即有哪些 `rs[i]` 落在该矩形中),再对线段进行求交集(总长度),即可计算出该矩形的「高度」,从而计算出来该矩形的面积。 |
| 54 | + |
| 55 | + |
| 56 | + |
| 57 | +Java 代码: |
| 58 | +```Java |
| 59 | +class Solution { |
| 60 | + int MOD = (int)1e9+7; |
| 61 | + public int rectangleArea(int[][] rs) { |
| 62 | + List<Integer> list = new ArrayList<>(); |
| 63 | + for (int[] info : rs) { |
| 64 | + list.add(info[0]); list.add(info[2]); |
| 65 | + } |
| 66 | + Collections.sort(list); |
| 67 | + long ans = 0; |
| 68 | + for (int i = 1; i < list.size(); i++) { |
| 69 | + int a = list.get(i - 1), b = list.get(i), len = b - a; |
| 70 | + if (len == 0) continue; |
| 71 | + List<int[]> lines = new ArrayList<>(); |
| 72 | + for (int[] info : rs) { |
| 73 | + if (info[0] <= a && b <= info[2]) lines.add(new int[]{info[1], info[3]}); |
| 74 | + } |
| 75 | + Collections.sort(lines, (l1, l2)->{ |
| 76 | + return l1[0] != l2[0] ? l1[0] - l2[0] : l1[1] - l2[1]; |
| 77 | + }); |
| 78 | + long tot = 0, l = -1, r = -1; |
| 79 | + for (int[] cur : lines) { |
| 80 | + if (cur[0] > r) { |
| 81 | + tot += r - l; |
| 82 | + l = cur[0]; r = cur[1]; |
| 83 | + } else if (cur[1] > r) { |
| 84 | + r = cur[1]; |
| 85 | + } |
| 86 | + } |
| 87 | + tot += r - l; |
| 88 | + ans += tot * len; |
| 89 | + ans %= MOD; |
| 90 | + } |
| 91 | + return (int) ans; |
| 92 | + } |
| 93 | +} |
| 94 | +``` |
| 95 | +TypeScript 代码: |
| 96 | +```TypeScript |
| 97 | +const MOD = BigInt(1e9+7) |
| 98 | +function rectangleArea(rs: number[][]): number { |
| 99 | + const list = new Array<number>() |
| 100 | + for (let info of rs) { |
| 101 | + list.push(info[0]); list.push(info[2]); |
| 102 | + } |
| 103 | + list.sort((a,b)=>a-b) |
| 104 | + let ans = 0n |
| 105 | + for (let i = 1; i < list.length; i++) { |
| 106 | + const a = list[i - 1], b = list[i], len = b - a |
| 107 | + if (len == 0) continue |
| 108 | + const lines = new Array<number[]>() |
| 109 | + for (let info of rs) { |
| 110 | + if (info[0] <= a && b <= info[2]) lines.push([info[1], info[3]]) |
| 111 | + } |
| 112 | + lines.sort((l1,l2)=>{ |
| 113 | + return l1[0] != l2[0] ? l1[0] - l2[0] : l1[1] - l2[1] |
| 114 | + }) |
| 115 | + let tot = 0n, l = -1, r = -1 |
| 116 | + for (let cur of lines) { |
| 117 | + if (cur[0] > r) { |
| 118 | + tot += BigInt(r - l) |
| 119 | + l = cur[0]; r = cur[1] |
| 120 | + } else if (cur[1] > r) { |
| 121 | + r = cur[1] |
| 122 | + } |
| 123 | + } |
| 124 | + tot += BigInt(r - l) |
| 125 | + ans += tot * BigInt(len) |
| 126 | + ans %= MOD |
| 127 | + } |
| 128 | + return Number(ans) |
| 129 | +}; |
| 130 | +``` |
| 131 | +Python 代码: |
| 132 | +```Python |
| 133 | +class Solution: |
| 134 | + def rectangleArea(self, rs: List[List[int]]) -> int: |
| 135 | + ps = [] |
| 136 | + for info in rs: |
| 137 | + ps.append(info[0]) |
| 138 | + ps.append(info[2]) |
| 139 | + ps.sort() |
| 140 | + ans = 0 |
| 141 | + for i in range(1, len(ps)): |
| 142 | + a, b = ps[i - 1], ps[i] |
| 143 | + width = b - a |
| 144 | + if width == 0: |
| 145 | + continue |
| 146 | + lines = [(info[1], info[3]) for info in rs if info[0] <= a and b <= info[2]] |
| 147 | + lines.sort() |
| 148 | + height, l, r = 0, -1, -1 |
| 149 | + for cur in lines: |
| 150 | + if cur[0] > r: |
| 151 | + height += r - l |
| 152 | + l, r = cur |
| 153 | + elif cur[1] > r: |
| 154 | + r = cur[1] |
| 155 | + height += r - l |
| 156 | + ans += height * width |
| 157 | + return ans % 1000000007 |
| 158 | +``` |
| 159 | +Go 代码: |
| 160 | +```Go |
| 161 | +const MOD = int64(1e9 + 7) |
| 162 | +func rectangleArea(rectangles [][]int) int { |
| 163 | + list := []int{} |
| 164 | + for _, info := range rectangles { |
| 165 | + list = append(list, info[0]) |
| 166 | + list = append(list, info[2]) |
| 167 | + } |
| 168 | + sort.Ints(list) |
| 169 | + ans := int64(0) |
| 170 | + for i := 1; i < len(list); i++ { |
| 171 | + a, b, length := list[i - 1], list[i], list[i] - list[i - 1] |
| 172 | + if length == 0 { |
| 173 | + continue |
| 174 | + } |
| 175 | + lines := [][]int{} |
| 176 | + for _, info := range rectangles { |
| 177 | + if info[0] <= a && b <= info[2] { |
| 178 | + lines = append(lines, []int{info[1], info[3]}) |
| 179 | + } |
| 180 | + } |
| 181 | + sort.Slice(lines, func(i,j int) bool { |
| 182 | + if lines[i][0] != lines[j][0] { |
| 183 | + return lines[i][0] - lines[j][0] < 0 |
| 184 | + } |
| 185 | + return lines[i][1] - lines[j][1] < 0 |
| 186 | + }) |
| 187 | + total, l, r := int64(0), -1, -1 |
| 188 | + for _, cur := range lines { |
| 189 | + if cur[0] > r { |
| 190 | + total += int64(r - l) |
| 191 | + l, r = cur[0], cur[1] |
| 192 | + } else if cur[1] > r { |
| 193 | + r = cur[1] |
| 194 | + } |
| 195 | + } |
| 196 | + total += int64(r - l) |
| 197 | + ans += total * int64(length) |
| 198 | + ans %= MOD |
| 199 | + } |
| 200 | + return int(ans) |
| 201 | +} |
| 202 | +``` |
| 203 | +* 时间复杂度:预处理所有扫描线的复杂度为 $O(n\log{n})$;处理所有相邻的扫描线,并计算相邻扫描线形成的矩形面积复杂度为 $O(n\log{n})$ 。整体复杂度为 $O(n^2\log{n})$ |
| 204 | +* 空间复杂度:$O(n)$ |
| 205 | + |
| 206 | +--- |
| 207 | + |
| 208 | +### 最后 |
| 209 | + |
| 210 | +这是我们「刷穿 LeetCode」系列文章的第 `No.850` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。 |
| 211 | + |
| 212 | +在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 |
| 213 | + |
| 214 | +为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 |
| 215 | + |
| 216 | +在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 |
| 217 | + |
0 commit comments