给你一个数组 rectangles ,其中 rectangles[i] = [xi, yi, ai, bi] 表示一个坐标轴平行的矩形。这个矩形的左下顶点是 (xi, yi) ,右上顶点是 (ai, bi) 。
如果所有矩形一起精确覆盖了某个矩形区域,则返回 true ;否则,返回 false 。
示例 1:
输入:rectangles = [[1,1,3,3],[3,1,4,2],[3,2,4,4],[1,3,2,4],[2,3,3,4]]
输出:true
解释:5 个矩形一起可以精确地覆盖一个矩形区域。
方法一:扫描线
因为需要判断的对象为正方形是否能构成完美矩形,故我们可将正方形简化储存为其平行于y轴的两条边,从而将题设改为判断除最外侧两边外,其余所有边两两重合且能构成一定长边。
public boolean isRectangleCover(int[][] rectangles) {
int n = rectangles.length;
// 储存矩形每一条边的位置 {x, y下, y上, 左/右}
int[][] store = new int[2*n][4];
for (int i = 0, index = 0; i < n; ++ i) {
int[] per = rectangles[i];
store[index ++] = new int[] {per[0], per[1], per[3], 1};
store[index ++] = new int[] {per[2], per[1], per[3], -1};
}
// 以边的下端进行升序排序
Arrays.sort(store, (store1, store2) -> {
if (store1[0] != store2[0]) return store1[0]-store2[0];
return store1[1] - store2[1];
});
n *= 2;
// 分别储存相同 x 下 [左边的线段] 和 [右边的线段]
List<int[]> lLeft = new ArrayList<>(), lRight = new ArrayList<>();
for (int left = 0; left < n;) {
int right = left;
lLeft.clear(); lRight.clear();
// 寻找横坐标相同的部分
while (right < n && store[left][0] == store[right][0]) ++ right;
for (int i = left; i < right; ++ i) {
int[] cur = new int[] {store[i][1], store[i][2]};
List<int[]> list = store[i][3] == 1 ? lLeft : lRight;
if (list.isEmpty()) {
list.add(cur);
} else {
int[] prev = list.get(list.size()-1);
if (cur[0] < prev[1]) return false; // 重叠
else if (cur[0] == prev[1]) prev[1] = cur[1]; // 首尾相连
else list.add(cur);
}
}
if (left > 0 && right < n) {
// 若不是完美矩形的边缘竖边,检查是否成对出现
if (lLeft.size() != lRight.size()) return false;
for (int i = 0; i < lLeft.size(); ++ i) {
if (lLeft.get(i)[0] == lRight.get(i)[0] && lLeft.get(i)[1] == lRight.get(i)[1]) continue;
return false;
}
} else {
// 若是完美矩形的边缘竖边,检查是否形成完整一段
if (lLeft.size() + lRight.size() != 1) return false;
}
left = right;
}
return true;
}