这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战
目标:拼搏30天,拿到11月的Leetcode 月度勋章。今天是第 16 天。
题目:完美矩阵
给你一个数组 rectangles ,其中 rectangles[i] = [xi, yi, ai, bi] 表示一个坐标轴平行的矩形。这个矩形的左下顶点是 (xi, yi) ,右上顶点是 (ai, bi) 。 如果所有矩形一起精确覆盖了某个矩形区域,则返回 true ;否则,返回 false 。
分析:
这道题目是一道hard题,但是最重要的不是解题思路,而是这道题目要处理的步骤太多了,稍有不慎就导致无法正常AC。我也是提交了4遍左右才成功通过所有的测试用例。
题目要求:所有矩形一起 精确 覆盖了某个矩形区域,可以得出以下几个结论:
- 不能出现空缺;
- 不能出现重叠;
- 所有小矩形合在一起是大矩形;
通过以上结论,我们可知,所有小矩形的面积之和等于大矩形的面积,所以,我们可以先找出大矩形的左下角和右上角,然后求出所有小矩形的面积之和与大矩形的面积比较,如果不相等,可以直接返回 false。
我们考虑拼出完美矩形的情况:
- 左下角、左上角、右下角、右上角的顶点一定是所有矩阵的最外圈
- 它们的面积和一定和完美矩阵的面积相等
任意不是这四个顶点的小矩阵的点,一定会出现两次或四次(如果出现四次以上,一定有超过四个矩阵以这个点为顶点,那么必然有重叠;如果出现奇数次,那么必然没有被完整覆盖)【这个的判断可以去掉面积相等,但是是内部重叠的情况】
解答:
class Solution {
public boolean isRectangleCover(int[][] rectangles) {
// 计算每个小矩形面积是否等于大矩形面积
// 看每个顶点出现的次数,如果最后出现一次的顶点不是四个,则说明不符合完美矩形
int area = 0;
Set<Integer> set = new HashSet<>();
// 记录大矩形的左下角和右上角
int a1 = Integer.MAX_VALUE, b1 = Integer.MAX_VALUE;
int a2 = Integer.MIN_VALUE, b2 = Integer.MIN_VALUE;
for (int[] rec : rectangles) {
// 小矩形的坐标
int x1 = rec[0];
int y1 = rec[1];
int x2 = rec[2];
int y2 = rec[3];
// 计算左下角
if (x1 < a1 || y1 < b1) {
a1 = x1;
b1 = y1;
}
// 计算右上角
if (x2 > a2 || y2 > b2) {
a2 = x2;
b2 = y2;
}
// 计算面积
area += (x2 - x1) * (y2 - y1);
// 记录每个顶点出现的次数
record(set, x1, y1);
record(set, x1, y2);
record(set, x2, y1);
record(set, x2, y2);
}
// 通过左下角和右上角坐标可以算出总面积
int totalArea = (a2 - a1) * (b2 - b1);
// 如果两个面积不相等,直接返回false
if (area != totalArea) return false;
// 四个为1的顶点正好是大矩形的四个顶点
return set.size() == 4 && set.contains(key(a1, b1)) && set.contains(key(a1, b2)) && set.contains(key(a2, b1)) && set.contains(key(a2, b2));
}’
private void record(Set<Integer> set, int x, int y) {
// 记录顶点出现的次数,如果一个顶点出现偶数次,则移除
int key = key(x, y);
if (set.contains(key)) {
set.remove(key);
} else {
set.add(key);
}
}
private int key(int x, int y) {
// 二维坐标转一维,方便比较
// 100000007是随便取的一个大质数
// 这里即使溢出了也没什么问题
return x * 100000007 + y;
}
}
复制代码
复杂度分析:
- 时间复杂度:O(n)
- 空间复杂度:O(n)