850.矩形面积 II 【矩形的分解】

题解

启发式解法-矩形分解

思路

因为多个矩形重叠部分的面积只需要计算一次,因此可以想到将重叠的多个矩形转化为多个不重叠的矩形,最后通过计算不重叠矩形中所有的矩形面积得到答案。
所需要的前置知识,针对任意两个矩形rec1, rec2

  1. 判断两个矩形是否重叠。两个矩形不重叠的条件为:rec1.x1 >= rec2.x2 || rec2.x1 >= rec1.x2 || rec1.y1 >= rec2.y2 || rec2.y1 >= rec1.y2,当满足其中至少一个条件时,说明一个矩形在另一个矩形的四周,不重叠。
  2. 当两个矩形重叠时,重叠部分也是一个矩形。设该矩形的坐标为:(ox1, oy1, ox2, oy2),有:
    ox1 = max(rec1.x1, rec2.x1)
    ox2 = min(rec1.x2, rec2.x2)
    oy1 = max(rec1,y1, rec2,y1)
    oy2 = min(rec1.y2, rec2.y2)

笔者最开始的想法为:
遍历给定的矩形数组,与之前所有的矩形判断一次是否重叠,若重叠,则减去重叠部分的面积。但这里会存在问题,因为减去重叠部分可能是之前矩形公共的部分,只需要减一次,但这种方法可能会减去多次,所以是错误的。
后参考了该题解受到了启发:python 不知道该叫什么方法。。

思路如下:

  1. 记录一个已知不重叠的矩形列表: unOverlapRectangleList,其中的元素为每个矩形的坐标,可以用对象来存也可以用数组来存,这个随意。该列表的特点就在于其中的矩形都是没有重叠的。
  2. 依次遍历输入的每个矩形rectangle[i],与unOverlapRectangleList中的所有矩形进行判断:
    • 若均不重叠则可以直接将其加入列表unOverlapRectangleList
    • 若与其中的某些矩形有重叠部分,则需要将rectangle[i]进行切分:
      • 将重叠的部分删除
      • 将不重叠的部分切分为多个矩形,并将这些矩形继续与unOverlapRectangleList后续的元素进行判断、切分。
      • rectangle[i]最终会被切分成多个不与unOverlapRectangleList中矩形重叠的小矩形,将这些小矩形加入unOverlapRectangleList中即可。
  3. 计算unOverlapRectangleList中每个矩形的面积,做累加求模,即可得到最后的答案。

那么又引出一个问题:如何将没有重叠的部分切分成多个小矩形呢?

因此:我们考虑矩形有重叠时可能出现的情况

矩形重叠情况

以下所有图中,重叠部分矩形用灰色阴影标出,rectangle[i]使用淡蓝色填充。

两个矩形的坐标为(其实有3个,但是第三个矩形的坐标只用于计算重叠矩形的坐标,后续用不到):

  • rectangle[i]的坐标为:(x1,y1,x2,y2)
  • 重叠矩形overlapRectangle的坐标为:(ox1, oy1, ox2, oy2)

切分只竖直切,水平切也可以,但是不是水平+竖直,否则矩形过多,较为繁杂。

  1. rectangle[i]完全包围了当前判断中的矩形,可以切分出四个小矩形的情况:

image1.jpg

通过已知的两个坐标我们可以得到切分出来的四个小矩形的坐标:

  • r1的坐标为:(x1, y1, ox1, y2)
  • r2的坐标为:(ox2, y1, x2, y2)
  • r3的坐标为:(ox1, oy2, ox2, y2)
  • r4的坐标为:(ox1, y1, ox2, oy1)
  1. 可以切分出三个小矩形的情况:

有以下四种,矩形编号仍与情况1中相同

image2.jpg

可以发现,r1,r2,r3,r4的坐标仍与情况1中的坐标相同

  • r1的坐标为:(x1, y1, ox1, y2)
  • r2的坐标为:(ox2, y1, x2, y2)
  • r3的坐标为:(ox1, oy2, ox2, y2)
  • r4的坐标为:(ox1, y1, ox2, oy1)

不同的地方在于,每一种情况中只有r1,r2,r3,r4中的其中三个:
如左上图只有r1,r3,r4,其中r2坐标因为ox2 = x2,导致矩形面积为0。因此我们只需要考虑面积不为0的矩形,剩下三种情况同理。

  1. 可以切分出两个小矩形的情况:

image3.jpg

同样的,r1,r2,r3,r4的坐标仍与上面的相同

  • r1的坐标为:(x1, y1, ox1, y2)
  • r2的坐标为:(ox2, y1, x2, y2)
  • r3的坐标为:(ox1, oy2, ox2, y2)
  • r4的坐标为:(ox1, y1, ox2, oy1)

而每一种情况中只有r1,r2,r3,r4中的其中两个:
如左上图只有r1,r4,其中r2坐标因为ox2 = x2,导致矩形面积为0;r3的坐标因为oy2=y2,导致面积为0。因此我们只需要考虑面积不为0的矩形,剩下三种情况同理。

  1. 可以切分出一个小矩形的情况:

image4.jpg

同样的,r1,r2,r3,r4的坐标仍与上面的相同

  • r1的坐标为:(x1, y1, ox1, y2)
  • r2的坐标为:(ox2, y1, x2, y2)
  • r3的坐标为:(ox1, oy2, ox2, y2)
  • r4的坐标为:(ox1, y1, ox2, oy1)

而每一种情况中只有r1,r2,r3,r4中的其中一个:
如左上图只有r1,其中r2的坐标因为ox2 = x2,导致矩形面积为0;r3的坐标因为oy2 = y2,导致面积为0;r4的坐标因为y1 = oy1,导致面积为0。因此我们只需要考虑面积不为0的矩形,剩下三种情况同理。

  1. 无法切分出小矩形的情况:

image5.jpg

这种情况仍可以得到四个矩形r1,r2,r3,r4,坐标与上述计算仍然相同,只是面积均为零,不需要做考虑。

代码实现

通过上述的思路,可以实现代码:

class Rectangle {
    public int x1;
    public int x2;
    public int y1;
    public int y2;

    public Rectangle(int x1, int y1, int x2, int y2) {
        this.x1 = x1;
        this.y1 = y1;
        this.x2 = x2;
        this.y2 = y2;
    }

    public long area(){
        return Rectangle.area(this);
    }

    public static long area(Rectangle rectangle){
        return Rectangle.area(new int[]{rectangle.x1, rectangle.y1, rectangle.x2, rectangle.y2});
    }

    public static long area(int[] rec){
        return (long) (rec[2] - rec[0]) * (rec[3] - rec[1]);
    }
}

public class Solution {
    public static final int MOD = 1000000007;
    private List<Rectangle> unOverlapRectangleList = new LinkedList<>();

    public int rectangleArea(int[][] rectangles) {
        long ans = 0;
        int n = rectangles.length;

        for (int i = 0; i < n; i++) {
            processOverlapRectangle(new Rectangle(rectangles[i][0], rectangles[i][1],rectangles[i][2],rectangles[i][3]));
        }

        for (Rectangle rectangle : unOverlapRectangleList) {
            ans += rectangle.area();
        }

        return (int) (ans % MOD);
    }

    private void processOverlapRectangle(Rectangle rectangle){
        //将当前的矩形切分成与已知矩形不重叠的多个小矩形,直至遍历完所有的已知矩形
        //最后将切分完的小矩形列表添加到已知矩形列表中
        List<Rectangle> newRectangles = new LinkedList<>();
        List<Rectangle> temp = new LinkedList<>();
        newRectangles.add(rectangle);

        for (Rectangle existRectangle : unOverlapRectangleList) {
            for (Rectangle newRectangle : newRectangles) {
                if (!isOverlap(newRectangle, existRectangle)){
                    //如果没有重叠部分,则添加到暂存的列表中,遍历下一个元素
                    temp.add(newRectangle);
                    continue;
                }

                //重叠了,则需要对当前新的矩形进行切分,将重叠部分删去,得到剩余部分的矩形分解
                //切分的结果为一个列表,其中的矩形元素与已知的矩形不重叠且面积大于0。
                List<Rectangle> segments = segment(newRectangle, existRectangle);
                temp.addAll(segments);

            }

            //将暂存列表中的所有矩形拿出来作为下一次迭代时判断的新矩形列表
            newRectangles.clear();
            newRectangles.addAll(temp);
            //清空暂存列表
            temp.clear();
        }

        //将切分出来的矩形加入已知不重复的矩形列表中
        unOverlapRectangleList.addAll(newRectangles);
    }

    public List<Rectangle> segment(Rectangle newRectangle, Rectangle existRectangle){
        //首先得到重叠部分矩形坐标
        Rectangle overlapRectangle = overlapRectangle(newRectangle, existRectangle);

        //根据重叠部分的坐标获得四个切分后的矩形
        LinkedList<Rectangle> segments = new LinkedList<>();
        segments.add(new Rectangle(newRectangle.x1, newRectangle.y1, overlapRectangle.x1, newRectangle.y2));
        segments.add(new Rectangle(overlapRectangle.x2, newRectangle.y1, newRectangle.x2, newRectangle.y2));
        segments.add(new Rectangle(overlapRectangle.x1, overlapRectangle.y2, overlapRectangle.x2, newRectangle.y2));
        segments.add(new Rectangle(overlapRectangle.x1, newRectangle.y1, overlapRectangle.x2, overlapRectangle.y1));

        //过滤掉面积为0的矩形
        return segments.stream().filter(rec-> rec.x1 != rec.x2 && rec.y1 != rec.y2).collect(Collectors.toList());
    }

    //判断两个矩形是否有重叠
    public boolean isOverlap(Rectangle rec1, Rectangle rec2){
        // xi1 >= xj2 || xj1 >= xi2 || yi1 >= yj2 || yj1 >= yi2
        // 一个矩形在另外一个矩形四周
        return !(rec1.x1 >= rec2.x2 || rec2.x1 >= rec1.x2 || rec1.y1 >= rec2.y2 || rec2.y1 >= rec1.y2);
    }

    // 得到重叠部分矩形的坐标
    public Rectangle overlapRectangle(Rectangle rec1, Rectangle rec2){
        int overLapAreaX1 = Math.max(rec1.x1, rec2.x1);
        int overLapAreaX2 = Math.min(rec1.x2, rec2.x2);
        int overLapAreaY1 = Math.max(rec1.y1, rec2.y1);
        int overLapAreaY2 = Math.min(rec1.y2, rec2.y2);

        return new Rectangle(overLapAreaX1,overLapAreaY1,overLapAreaX2,overLapAreaY2);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
836. 矩形重叠是一道经典的计算几何问题。给定两个矩形的左下角和右上角坐标,我们需要判断这两个矩形是否重叠。 要判断两个矩形是否重叠,我们可以比较它们的位置关系。如果第一个矩形的右上角坐标小于第二个矩形的左下角坐标,或者第一个矩形的左下角坐标大于第二个矩形的右上角坐标,那么这两个矩形是不重叠的。反之,如果两个矩形的左下角和右上角坐标都满足条件,那么它们就是重叠的。 我们可以用以下伪代码来实现这个判断过程: function isRectangleOverlap(rectangle1, rectangle2): if rectangle1.top < rectangle2.bottom or rectangle1.bottom > rectangle2.top: return false if rectangle1.right < rectangle2.left or rectangle1.left > rectangle2.right: return false return true 其中,rectangle1和rectangle2分别代表两个矩形的左下角和右上角坐标。top、bottom、left和right分别代表矩形的上边界、下边界、左边界和右边界。 通过这个算法,我们就可以判断两个矩形是否重叠了。不过需要注意的是,对于矩形重叠的问题,不同人可能有不同的定义。在本算法中,我们采用的是矩形边界不重叠的定义。如果边界重叠也算作矩形重叠的话,只需要将上述判断条件中的小于号和大于号改为小于等于号和大于等于号即可。 总之,矩形重叠问题可以通过比较矩形的坐标来判断。这是一道简单但常见的计算几何问题,我们可以用上述算法快速解决。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值