[LeetCode解题报告] 850. 矩形面积 II

一、 题目

1. 题目描述

我们给出了一个(轴对齐的)二维矩形列表 rectangles 。 对于 rectangle[i] = [x1, y1, x2, y2],其中(x1,y1)是矩形 i 左下角的坐标, (xi1, yi1) 是该矩形 左下角 的坐标, (xi2, yi2) 是该矩形 右上角 的坐标。

计算平面中所有 rectangles 所覆盖的 总面积 。任何被两个或多个矩形覆盖的区域应只计算 一次

返回 总面积 。因为答案可能太大,返回 109 + 7 的  。

 

示例 1:

输入:rectangles = [[0,0,2,2],[1,0,2,3],[1,0,3,1]]
输出:6
解释:如图所示,三个矩形覆盖了总面积为6的区域。
从(1,1)到(2,2),绿色矩形和红色矩形重叠。
从(1,0)到(2,3),三个矩形都重叠。

示例 2:

输入:rectangles = [[0,0,1000000000,1000000000]]
输出:49
解释:答案是 1018 对 (109 + 7) 取模的结果, 即 49 。

 

提示:

  • 1 <= rectangles.length <= 200
  • rectanges[i].length = 4
  • 0 <= xi1, yi1, xi2, yi2 <= 109
  • 矩形叠加覆盖后的总面积不会超越 2^63 - 1 ,这意味着可以用一个 64 位有符号整数来保存面积结果。
Related Topics
  • 线段树
  • 数组
  • 有序集合
  • 扫描线

  • 👍 102
  • 👎 0
### 2. 原题链接 链接: [850. 矩形面积 II](https://leetcode.cn/problems/rectangle-area-ii/)

二、 解题报告

1. 思路分析

这道题是线段树处理矩形面积并模板题。
按x顺序遍历每一条竖边,累计入度,矩形的左边是入边,右边是出边。
入度只会是正数或0。
用线段树维护当前y上覆盖距离,乘上x间距即可

当然不用线段树也有,有很多种解法。

2. 复杂度分析

最坏时间复杂度O(nlog2n)

3. 代码实现

线段树


""" 题解
线段树处理矩形面积并模板题,对y要离散化
一定要注意语法!!
相当于通过线段树的归并,永远更新根节点,用根节点计算当前覆盖的长度。
"""
class IntervalTreeNode:
    def __init__(self, len, cover):
        self.len = len
        self.cover = cover

class IntervalTree:
    def __init__(self, size, ys=None):
        self.size = size
        # self.interval_tree = [IntervalTreeNode(0,0)]*(size*4)     ## 这个地方wa了很久,不能这么写,一直更新一个实例
        self.interval_tree = [IntervalTreeNode(0, 0) for _ in range(size * 4)]
        self.ys = ys

    def update_from_son(self, p, l, r):
        interval_tree = self.interval_tree
        pn = interval_tree[p]
        if pn.cover > 0:
            pn.len = self.ys[r - 1] - self.ys[l - 1]
        else:
            if l + 1 == r:
                pn.len = 0
            else:
                pn.len = interval_tree[p * 2].len + interval_tree[p * 2 + 1].len

    def insert(self, p, l, r, x, y, cover):
        interval_tree = self.interval_tree
        if x <= l and r <= y:
            interval_tree[p].cover += cover
            self.update_from_son(p, l, r)
            return
        mid = (l + r) // 2
        if x < mid:
            self.insert(p * 2, l, mid, x, y, cover)
        if y > mid:
            self.insert(p * 2 + 1, mid, r, x, y, cover)
        self.update_from_son(p, l, r)

class LineY:
    def __init__(self, x, y1, y2, cover):
        self.x = x
        self.y1 = y1
        self.y2 = y2
        self.cover = cover

class Solution:
    def rectangleArea(self, rectangles: List[List[int]]) -> int:
        lines = []  # 所有竖线线段
        ys = set()  # 离散化
        for x1, y1, x2, y2 in rectangles:
            lines.append(LineY(x1, y1, y2, 1))
            lines.append(LineY(x2, y1, y2, -1))
            ys.add(y1)
            ys.add(y2)
        lines.sort(key=lambda x: x.x)
        line_count = len(lines)
        ys = list(ys)
        ys.sort()

        interval_tree = IntervalTree(line_count, ys=ys)

        ans = 0
        mod = int(1e9 + 7)
        for i in range(0, line_count):
            line = lines[i]
            # print(line.x,line.y1,line.y2,line.cover)
            y1 = bisect_left(ys, line.y1)
            y2 = bisect_left(ys, line.y2)
            # if y1==y2:
            #     continue
            if i > 0:
                ans += (line.x - lines[i - 1].x) * interval_tree.interval_tree[1].len
                ans %= mod
            interval_tree.insert(1, 1, len(ys), y1 + 1, y2 + 1, line.cover)

        return ans

	# 注意以下是n方算法,n大了过不了,这种还是适合辛普森的题
class Line:
    def __init__(self,l,r):
        self.l,self.r=l,r
    def __lt__(self,other):  # 线段按起点排序
        return self.l<other.l
class Rect:
    def __init__(self,x1,y1,x2,y2):
        self.x1,self.y1,self.x2,self.y2=x1,y1,x2,y2
    def __lt__(self,other):  # 方块按左边界排序
        return self.x1<other.x1

class Solution2:
    def rectangleArea(self, rectangles: List[List[int]]) -> int:
        size = len(rectangles)
        rects = []  # 所有方块
        xs = set()  # 统计
        for x1, y1, x2, y2 in rectangles:
            rects.append(Rect( x1, y1, x2, y2 ))
            xs.add(x1)
            xs.add(x2)
        xs = sorted(list(xs))

        def sort_rect_by_left(rects):
            a = ([(rect.x1,rect) for rect in rects])
            a.sort()
            return [rect for x,rect in a]

        # rects = sort_rect_by_left(rects)
        rects.sort()
        def sort_line_by_l(lines):
            a = sorted([(line.l,line) for line in lines])
            return [line for _ ,line in a]

        def get_f(x):
            lines = []
            for rect in rects:
                x1,y1,x2,y2 = rect.x1,rect.y1,rect.x2,rect.y2
                if x<=x1:  # 这个方块在线右边,后边不用算了
                    break
                if x2<=x:  # 这个方块在线的左边,它自己不用算
                    continue
                lines.append(Line(y1,y2))
            # lines = sort_line_by_l(lines)
            lines.sort()
            line_size = len(lines)
            f = 0
            i = 0
            while i < line_size:
                r = lines[i].r
                j = i + 1
                while j < line_size:
                    if lines[j].l > r:
                        break
                    r = max(r, lines[j].r)
                    j += 1
                f += r - lines[i].l
                i = j
            return f
        ans = 0
        mod = 10**9 + 7
        for i in range(1, len(xs)):
            width = xs[i] - xs[i-1]
            x = xs[i-1]+ width/2
            ans += width * get_f(x)
            ans %= mod
        return ans

三、 本题小结

  1. 线段树计算矩形面积并
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值