題目
給你 二維 平面上兩個 由直線構成且邊與座標軸平行/垂直 的矩形,請你計算並返回兩個矩形覆蓋的總面積。
每個矩形由其 左下 頂點和 右上 頂點座標表示:
第一個矩形由其左下頂點 (ax1, ay1) 和右上頂點 (ax2, ay2) 定義。
第二個矩形由其左下頂點 (bx1, by1) 和右上頂點 (bx2, by2) 定義。
示例 1:
Rectangle Area
輸入:ax1 = -3, ay1 = 0, ax2 = 3, ay2 = 4, bx1 = 0, by1 = -1, bx2 = 9, by2 = 2
輸出:45
示例 2:
輸入:ax1 = -2, ay1 = -2, ax2 = 2, ay2 = 2, bx1 = -2, by1 = -2, bx2 = 2, by2 = 2
輸出:16
提示:
-10^4 <= ax1, ay1, ax2, ay2, bx1, by1, bx2, by2 <= 10^4
v1-重疊面積
思路
如果矩形不重疊,就是 2 個矩形的面積之和。
如果重合,計算出重疊面積,就是原始的面積之和-重疊面積。
如果兩個矩形重疊,那麼重疊的面積一定大於0.
於是問題變成如何計算重疊的矩形面積?
如果有重疊,重疊的部分也一定是一個矩形
交集面積的計算方式是基於兩個矩形的重疊區域的 左下角和右上角的座標 來推算的。
具體來說,兩個矩形的交集矩形(如果存在)也是一個矩形,它的邊界由兩個矩形的邊界決定。
計算重疊面積
-
第一個矩形
rec1 = [x1, y1, x2, y2]
,表示其左下角座標為(x1, y1)
,右上角座標為(x2, y2)
。 -
第二個矩形
rec2 = [x3, y3, x4, y4]
,表示其左下角座標為(x3, y3)
,右上角座標為(x4, y4)
。
如果兩個矩形有交集,那麼交集的矩形的左下角和右上角的座標可以透過取兩者的最大和最小值來確定:
-
交集矩形的左下角:
(x1', y1') = (max(x1, x3), max(y1, y3))
-
交集矩形的右上角:
(x2', y2') = (min(x2, x4), min(y2, y4))
判斷交集是否存在
交集矩形的面積只有在其寬度和高度都大於 0 時才存在。
如果交集矩形的寬度或高度小於等於 0,說明兩個矩形沒有交集。
因此,交集矩形的寬度和高度的計算方式如下:
- 寬度:
width = x2' - x1'
- 高度:
height = y2' - y1'
交集矩形的面積則為:
- 面積 =
width * height
,但是如果寬度或高度小於等於 0,面積就為 0。
因此,計算交集的面積時,我們需要確保 width > 0
和 height > 0
,否則交集的面積為 0。
結果
完整的面積 = 兩個矩形面積之和 - 重疊面積
程式碼示例
class Solution {
public int computeArea(int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2) {
int rect1 = rectangleArea(ax1, ay1, ax2, ay2);
int rect2 = rectangleArea(bx1, by1, bx2, by2);
int overlap = rectangleOverlapArea(new int[]{ax1, ay1, ax2, ay2}, new int[]{bx1, by1, bx2, by2});
return rect1-overlap+rect2;
}
private int rectangleArea(int ax1, int ay1, int ax2, int ay2) {
return (ax2-ax1) * (ay2-ay1);
}
public int rectangleOverlapArea(int[] rec1, int[] rec2) {
// 計算交集矩形的左下角和右上角
int x1 = Math.max(rec1[0], rec2[0]);
int y1 = Math.max(rec1[1], rec2[1]);
int x2 = Math.min(rec1[2], rec2[2]);
int y2 = Math.min(rec1[3], rec2[3]);
// 計算交集的寬度和高度
int width = x2 - x1;
int height = y2 - y1;
// 如果交集的寬度和高度都大於 0,說明有重疊
if (width > 0 && height > 0) {
return width * height;
}
// 沒有重疊
return 0;
}
}
效果
2ms 擊敗21.43%
小結
這種解法相對對比較自然。
v2-掃描線
思路
按照 x 軸,將整體的矩形面積,按照每一段 x 拆分為小矩形。
最後的和就是整體矩形之和。
解法
public int computeArea(int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2) {
int[][] arr = new int[2][];
arr[0] = new int[]{ax1, ay1, ax2, ay2};
arr[1] = new int[]{bx1, by1, bx2, by2};
Arrays.sort(arr, (o1, o2) -> o1[1] == o2[1] ? o1[3] - o2[3] : o1[1] - o2[1]);
List<Integer> xList = Arrays.asList(ax1, ax2, bx1, bx2);
Collections.sort(xList);
int ans = 0;
for (int i = 1; i < 4; i++) {
int width = xList.get(i) - xList.get(i-1);
for (int[] ints : getLine(xList.get(i-1), xList.get(i), arr)) {
ans += width * (ints[1] - ints[0]);
}
}
return ans;
}
private List<int[]> getLine(int x1, int x2, int[][] arr) {
List<int[]> list = new ArrayList<>();
for (int[] ints : arr) {
if (x1 >= ints[0] && x2 <= ints[2]) {
if (list.isEmpty()) {
list.add(new int[]{ints[1], ints[3]});
} else {
int[] tmp = list.get(list.size() - 1);
if (tmp[1] < ints[1]) {
list.add(new int[]{ints[1], ints[3]});
} else if (tmp[1] < ints[3]) {
tmp[1] = ints[3];
}
}
}
}
return list;
}
效果
6ms 擊敗 4.76%
小結
整體上而言,我比較喜歡重疊面積的方式,這種比較好想到,而且可以擴充套件。
當然掃描線也是一種很通用的解法。
這一題的巧思屬於維度投影的解法,很巧妙。
開源地址
為了便於大家學習,所有實現均已開源。歡迎 fork + star~
https://github.com/houbb/leetcode
掃描線專題
leetcode 掃描線專題 06-掃描線演算法(Sweep Line Algorithm)
leetcode 掃描線專題 06-leetcode.218 the-skyline-problem 力扣.218 天際線問題
leetcode 掃描線專題 06-leetcode.252 meeting room 力扣.252 會議室
leetcode 掃描線專題 06-leetcode.253 meeting room ii 力扣.253 會議室 II
leetcode 掃描線專題 06-leetcode.1851 minimum-interval-to-include-each-query 力扣.1851 包含每個查詢的最小區間
leetcode 掃描線專題 06-leetcode.223 rectangle-area 力扣.223 矩形面積
leetcode 掃描線專題 06-leetcode.3047 find-the-largest-area-of-square-inside-two-rectangles 力扣.3047 求交集區域的最大正方形面積
leetcode 掃描線專題 06-leetcode.391 perfect-rectangle 力扣.391 完美矩形
leetcode 掃描線專題 06-leetcode.836 rectangle-overlap 力扣.836 矩形重疊
leetcode 掃描線專題 06-leetcode.850 rectangle-area 力扣.850 矩形面積 II