LeetCode - LineSweep - 850. 矩形面积 II

题目 850. 矩形面积 II

难度 困难

我们给出了一个(轴对齐的)矩形列表 rectangles 。 对于 rectangle[i] = [x1, y1, x2, y2],
其中(x1,y1)是矩形 i 左下角的坐标,(x2,y2)是该矩形右上角的坐标。
找出平面中所有矩形叠加覆盖后的总面积。 由于答案可能太大,请返回它对 10 ^ 9 + 7 取模的结果。

示例1:

输入:[[0,0,2,2],[1,0,2,3],[1,0,3,1]]
输出:6

示例2:

输入:[[0,0,1000000000,1000000000]]
输出:49
解释:答案是 10^18 对 (10^9 + 7) 取模的结果, 即 (109)2 → (-7)^2 = 49 。

提示:

  • 1 <= rectangles.length <= 200
  • rectanges[i].length = 4
  • 0 <= rectangles[i][j] <= 10^9
  • 矩形叠加覆盖后的总面积不会超越 2^63 - 1 ,这意味着可以用一个 64 位有符号整数来保存面积结果。

刚开始拿到这道题,我想了15分钟左右,没想出来怎么弄,想通过最笨的方法一个一个扫描然后计算总面积,发现重叠的部分不知道怎么去处理,所以关键就是如何去处理才能够重复计算重叠的面积。官方的解法写的我有些实在看不懂,这里给出别人比较好理解的解法

解法1 - 扫描法

如果我们是顺序处理rectangles,每次处理一个矩形,那么重复计算将变得非常难以处理。为此我们将矩形面价按照x轴(或者y轴)进行切割,然后再在y轴(或者x轴)进行累计求和。下面将选择在x轴方向进行切割。

算法描述:

第一步:将所有矩形的x坐标放入xVec
第二步:按照升序排序x坐标
第三步:在xVec中进行去重
第四步:逆序扫描x坐标段,计算在(xVec[i], xVec[i + 1])段中的面积假设xVec[i] = 1, xVec[i + 1] = 2,则所
有矩形在这个x区间段中存在交集的矩形y轴段是矩形[0,0,2,2]中的Y轴区间段[0, 2],矩形
[1,0,2,3]中的Y轴区间段[0, 3],矩形[1,0,3,1]中的Y轴区间段[0,1],合并时候Y轴区间段段为[0, 3]
(类似区间的合并)distanceX = xVec[i + 1] - xVec[i] = 1, distanceY = 3 - 0 = 3,所以S[2] =
distanceX * distanceY = 3

在这里插入图片描述

c++ 代码

class Solution {
public:
    int rectangleArea(vector<vector<int>>& rectangles) {
        vector<int> xVec;//用于存放所有矩形的x坐标
        int sumArea = 0, mod = 7 + 1e9;
        //第一步:将所有矩形的x坐标放入xVec
        for (const auto &rectangle : rectangles){
            xVec.push_back(rectangle[0]);
            xVec.push_back(rectangle[2]);
        }
        //第二步:按照升序排序x坐标
        sort(xVec.begin(), xVec.end());
        //第三步:在xVec中进行去重(unique是STL中的去重函数,它会把重复的元素移动到xVec的后端,并且返回第一个重复的值的迭代器)
        xVec.erase(unique(xVec.begin(), xVec.end()), xVec.end());
        //第四步:逆序扫描x坐标段,计算在(xVec[i], xVec[i + 1])段中的面积
        for (int i = xVec.size() - 2; i >= 0; --i){
            list<pair<int, int>> myList;//按照递增的顺序存储所有不重复、重叠的y轴坐标段
            //如果rectangle在(xVec[i], xVec[i + 1])段中有面积,则将它两个y轴坐标放入list
            for (const auto &rectangle : rectangles){
                if (rectangle[0] <= xVec[i] && rectangle[2] >= xVec[i + 1]){
                    addRange(myList, rectangle[1], rectangle[3]);
                }
            }
            //然后我们需要把y轴这些区间段的长度进行求和
            long distanceY = 0;
            for (const auto &item : myList){ //auto关键字可以自动判断数据类型
                distanceY += item.second - item.first;
            }
            //xVec[i + 1] - xVec[i]表示x轴区间段的宽度,distanceY是在这个x轴段y轴各个区间段长度的和
            sumArea = (sumArea + (xVec[i + 1] - xVec[i]) * distanceY) % mod;
        }
        return sumArea;
    }
    //添加Range[left, right]
	void addRange(list<pair<int, int>> &myList, int left, int right) {
		auto it = myList.begin();
        //第一步:确定[left,right]插入的位置(第一个与它有交集的元素)
		while (it != myList.end() && it->second < left) {
			// it->second 就是 rectangle[3]
			++it;
		}
        //第二步:判断是插入到list中还是修改list
        //如果是插入尾端、或者两个Range之间(list中没有Range与它有交集),直接插入
		if (it == myList.end() || it->first > right) {
			//it->firse 就是 rectangle[1]
			myList.insert(it, { left, right});
		}
		else {
            //此时修改it指向的元素
			it->first = min(it->first, left);
			it->second = max(it->second, right);
            //然后看it后面是否有需要合并的Rande
			auto beforeIt = it++;
			while (it != myList.end() && beforeIt->second >= it->first) {
				beforeIt->second = max(it->second, beforeIt->second);
				it = myList.erase(it);
			}
		}
	}
};

解法2 - 官方解法 - 坐标压缩法

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值