LeetCode刷题笔记_391. 完美矩形

题目描述

给你一个数组 rectangles ,其中 rectangles[i] = [xi, yi, ai, bi] 表示一个坐标轴平行的矩形。这个矩形的左下顶点是 (xi, yi) ,右上顶点是 (ai, bi) 。

如果所有矩形一起精确覆盖了某个矩形区域,则返回 true ;否则,返回 false 。

输入:rectangles = [[1,1,3,3],[3,1,4,2],[1,3,2,4],[3,2,4,4]]
输出:false
解释:图形顶端留有空缺,无法覆盖成一个矩形。

思路

需要满足3个条件

  1. 所有小矩形的和和大矩形面积相等
  2. 未重合的坐标只有4个
  3. 未重合的4个坐标是最外边的4个。
class Solution {
    public boolean isRectangleCover(int[][] rectangles) {
        int totalArea = 0;
        int a = 0x3f3f3f3f, b = 0x3f3f3f3f;
        int c = -0x3f3f3f3f, d = -0x3f3f3f3f;
        Set coord = new HashSet();
        for (int i = 0; i < rectangles.length; i++) {
            int[] rectangle = rectangles[i];
            totalArea += computeArea(rectangle[0], rectangle[1], rectangle[2], rectangle[3]);
            a = Math.min(a, rectangle[0]);
            b = Math.min(b, rectangle[1]);
            c = Math.max(c, rectangle[2]);
            d = Math.max(d, rectangle[3]);

            String m = Integer.toString(rectangle[0]) + Integer.toString(rectangle[1]);
            String n = Integer.toString(rectangle[0]) + Integer.toString(rectangle[3]);
            String p = Integer.toString(rectangle[2]) + Integer.toString(rectangle[3]);
            String q = Integer.toString(rectangle[2]) + Integer.toString(rectangle[1]);

            if (coord.contains(m)) {
                coord.remove(m);
            } else {
                coord.add(m);
            }
            if (coord.contains(n)) {
                coord.remove(n);
            } else {
                coord.add(n);
            }
            if (coord.contains(p)) {
                coord.remove(p);
            } else {
                coord.add(p);
            }
            if (coord.contains(q)) {
                coord.remove(q);
            } else {
                coord.add(q);
            }
        }

        String m = Integer.toString(a) + Integer.toString(b);
        String n = Integer.toString(c) + Integer.toString(b);
        String p = Integer.toString(c) + Integer.toString(d);
        String q = Integer.toString(c) + Integer.toString(d);
        if (coord.contains(m) && coord.contains(n) && coord.contains(p) && coord.contains(q)) {
            return (totalArea == computeArea(a, b, c, d)) && (coord.size() == 4);
        }
        return false;
    }

    public int computeArea(int x1, int y1, int x2, int y2) {
        return (x2 - x1) * (y2 - y1);
    }
}

 代码写的有点长,可能比较冗余,但是思路还是清晰的。
在遍历数组 rectangles 的过程中,不断记录 4 个 边界,作为最后大矩形;同时将各个小矩形的4个边界坐标放入 set 中,如果有一样的,就去除,这样可以保证最后集合中的是大矩形边界的点。

最后判断上面所说的三个条件就可以了。

题解

扫描线

将每个矩形 rectangles[i]rectangles[i] 看做两条竖直方向的边,使用 (x, y1, y2)(x,y1,y2) 的形式进行存储(其中 y1y1 代表该竖边的下端点,y2y2 代表竖边的上端点),同时为了区分是矩形的左边还是右边,再引入一个标识位,即以四元组 (x, y1, y2, flag)(x,y1,y2,flag) 的形式进行存储。

一个完美矩形的充要条件为:对于完美矩形的每一条非边缘的竖边,都「成对」出现(存在两条完全相同的左边和右边重叠在一起);对于完美矩形的两条边缘竖边,均独立为一条连续的(不重叠)的竖边。

如图(红色框的为「完美矩形的边缘竖边」,绿框的为「完美矩形的非边缘竖边」):
在这里插入图片描述

class Solution {
    public boolean isRectangleCover(int[][] rectangles) {
        int n = rectangles.length;
        int[][] rs = new int[n * 2][4];
        for (int i = 0, idx = 0; i < n; i++) {
            int[] re = rectangles[i];
            rs[idx++] = new int[]{re[0], re[1], re[3], 1};
            rs[idx++] = new int[]{re[2], re[1], re[3], -1};
        }
        Arrays.sort(rs, (a,b)->{
            if (a[0] != b[0]) return a[0] - b[0];
            return a[1] - b[1];
        });
        n *= 2;
        // 分别存储相同的横坐标下「左边的线段」和「右边的线段」 (y1, y2)
        List<int[]> l1 = new ArrayList<>(), l2 = new ArrayList<>(); 
        for (int l = 0; l < n; ) {
            int r = l;
            l1.clear(); l2.clear();
            // 找到横坐标相同部分
            while (r < n && rs[r][0] == rs[l][0]) r++;
            for (int i = l; i < r; i++) {
                int[] cur = new int[]{rs[i][1], rs[i][2]};
                List<int[]> list = rs[i][3] == 1 ? l1 : l2;
                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 (l > 0 && r < n) {
                // 若不是完美矩形的边缘竖边,检查是否成对出现
                if (l1.size() != l2.size()) return false;
                for (int i = 0; i < l1.size(); i++) {
                    if (l1.get(i)[0] == l2.get(i)[0] && l1.get(i)[1] == l2.get(i)[1]) continue;
                    return false;
                }
            } else {
                // 若是完美矩形的边缘竖边,检查是否形成完整一段
                if (l1.size() + l2.size() != 1) return false;
            }
            l = r;
        }
        return true;
    }
}

哈希表

class Solution {
    public boolean isRectangleCover(int[][] rectangles) {
        long area = 0;
        int minX = rectangles[0][0], minY = rectangles[0][1], maxX = rectangles[0][2], maxY = rectangles[0][3];
        Map<Point, Integer> cnt = new HashMap<Point, Integer>();
        for (int[] rect : rectangles) {
            int x = rect[0], y = rect[1], a = rect[2], b = rect[3];
            area += (long) (a - x) * (b - y);

            minX = Math.min(minX, x);
            minY = Math.min(minY, y);
            maxX = Math.max(maxX, a);
            maxY = Math.max(maxY, b);

            Point point1 = new Point(x, y);
            Point point2 = new Point(x, b);
            Point point3 = new Point(a, y);
            Point point4 = new Point(a, b);

            cnt.put(point1, cnt.getOrDefault(point1, 0) + 1);
            cnt.put(point2, cnt.getOrDefault(point2, 0) + 1);
            cnt.put(point3, cnt.getOrDefault(point3, 0) + 1);
            cnt.put(point4, cnt.getOrDefault(point4, 0) + 1);
        }

        Point pointMinMin = new Point(minX, minY);
        Point pointMinMax = new Point(minX, maxY);
        Point pointMaxMin = new Point(maxX, minY);
        Point pointMaxMax = new Point(maxX, maxY);
        if (area != (long) (maxX - minX) * (maxY - minY) || cnt.getOrDefault(pointMinMin, 0) != 1 || cnt.getOrDefault(pointMinMax, 0) != 1 || cnt.getOrDefault(pointMaxMin, 0) != 1 || cnt.getOrDefault(pointMaxMax, 0) != 1) {
            return false;
        }

        cnt.remove(pointMinMin);
        cnt.remove(pointMinMax);
        cnt.remove(pointMaxMin);
        cnt.remove(pointMaxMax);

        for (Map.Entry<Point, Integer> entry : cnt.entrySet()) {
            int value = entry.getValue();
            if (value != 2 && value != 4) {
                return false;
            }
        }
        return true;
    }
}

class Point {
    int x;
    int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public int hashCode() {
        return x + y;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Point) {
            Point point2 = (Point) obj;
            return this.x == point2.x && this.y == point2.y;
        }
        return false;
    }
}

参考资料

https://leetcode-cn.com/problems/perfect-rectangle/solution/c-zui-tong-su-yi-dong-si-lu-by-heroding-npgj/
https://leetcode-cn.com/problems/perfect-rectangle/solution/gong-shui-san-xie-chang-gui-sao-miao-xia-p4q4/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wei *

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值