# 850. Rectangle Area II

**<font color=red>难度: hard</font>**

## 刷题内容

> 原题连接

* https://leetcode.com/problems/rectangle-area-ii/

> 内容描述

```
We are given a list of (axis-aligned) rectangles.  Each rectangle[i] = [x1, y1, x2, y2] , where (x1, y1) are the coordinates of the bottom-left corner, and (x2, y2) are the coordinates of the top-right corner of the ith rectangle.

Find the total area covered by all rectangles in the plane.  Since the answer may be too large, return it modulo 10^9 + 7.



Example 1:

Input: [[0,0,2,2],[1,0,2,3],[1,0,3,1]]
Output: 6
Explanation: As illustrated in the picture.
Example 2:

Input: [[0,0,1000000000,1000000000]]
Output: 49
Explanation: The answer is 10^18 modulo (10^9 + 7), which is (10^9)^2 = (-7)^2 = 49.
Note:

1 <= rectangles.length <= 200
rectanges[i].length = 4
0 <= rectangles[i][j] <= 10^9
The total area covered by all rectangles will never exceed 2^63 - 1 and thus will fit in a 64-bit signed integer.
```

## 解题方案

> 思路 1
******- 时间复杂度: O(N^2lgN)******- 空间复杂度: O(N)******

参考[Java TreeMap solution inspired by Skyline and Meeting Room](https://leetcode.com/problems/rectangle-area-ii/discuss/137941/Java-TreeMap-solution-inspired-by-Skyline-and-Meeting-Room)

本来时间复杂度是O(N^2)，但是由于Python里面没有TreeMap，自己写了一下toTreeMap函数，时间变成O(N^2lgN)

cal_y函数最难理解

```python
class Solution:
    def rectangleArea(self, rectangles: 'List[List[int]]') -> 'int':
        M = 10**9 + 7
        points = []
        for r in rectangles:
            points.append((r[0], r[1], 1))
            points.append((r[0], r[3], -1))
            points.append((r[2], r[1], -1))
            points.append((r[2], r[3], 1))
        points.sort(key = lambda p: (p[0], -p[1]))
        # print(points)
        lookup = {}
        prex, prey, res = -1, -1, 0
        for i in range(len(points)):
            p = points[i]
            lookup[p[1]] = lookup.get(p[1], 0) + p[2]
            lookup = self.toTreeMap(lookup)
            # print(p)
            # print(lookup)
            if i == len(points) - 1 or points[i+1][0] > p[0]:
                if prex > -1:
                    res += (prey * (p[0] - prex)) % M
                    res %= M
                prey = self.cal_y(lookup)
                # print(prey)
                prex = p[0]
        return res
    
    def cal_y(self, maps):
        res, pre, cnt = 0, -1, 0
        for k, v in maps.items():
            if cnt > 0:
                res += k - pre
            cnt += v
            pre = k
        return res
    
    def toTreeMap(self, maps):
        treemap = collections.OrderedDict()
        for key in sorted(maps.keys()):
            treemap[key] = maps.get(key)
        return treemap
```

> 思路 2
******- 时间复杂度: O(N^2)******- 空间复杂度: O(N)******

参考[[C++/Python] Discretization and O(NlogN)](https://leetcode.com/problems/rectangle-area-ii/discuss/137914/C%2B%2BPython-Discretization-and-O(NlogN))

solution来自[yidong_w](https://leetcode.com/yidong_w/)

个人认为他的solution更容易理解一些


```
I found your code is a little difficult for me to understand, 
so I took your idea and wrote a similar solution.
It is scanning level by level. It will add to the result once it reach to the next level. 
Any sub rectangles that has count > 0 will be counted as valid rectangle.
```

```python
from heapq import heappush, heappop

class Solution:
    def rectangleArea(self, rectangles: 'List[List[int]]') -> 'int':
        x, pq = set(), []
        for rec in rectangles:
            x.add(rec[0])
            x.add(rec[2])
            heappush(pq, (rec[1], rec[0], rec[2], 1))
            heappush(pq, (rec[3], rec[0], rec[2], -1))
        
        x = sorted(list(x))
        xi = {v:i for i,v in enumerate(x)}
        count = [0] * len(x)
        # print(pq)
        # print(x)
        # print(xi)
        
        res = 0
        last_y = cur_y = pq[0][0]
        
        while pq:
            cur_y = pq[0][0]

            for i in range(len(x)):
                
                if count[i] > 0:
                    # print('x: ', i)
                    res += (x[i+1] - x[i]) * (cur_y - last_y)
                    res %= (10**9 + 7)
            # print(res)
    
            while pq and pq[0][0] == cur_y:
                cur_y, x1, x2, bound = heappop(pq)
                # 这里右边界蛮关键的，最后一个i的count不用加上bound，这与第29行的x[i+1]有关，否则会out of bound
                for i in range(xi[x1], xi[x2]): 
                    count[i] += bound
            
            last_y = cur_y
            # print(count)
        return res
```


> 思路 3
******- 时间复杂度: O(N^2)******- 空间复杂度: O(N^2)******

参考[wisdompeak](https://github.com/wisdompeak/LeetCode/tree/master/Others/850.Rectangle-Area-II)

此题的思路是，将题目所给的所有矩形的坐标点都集中起来组成一个网格。具体来说，将所有的X坐标集中起来（要去除重复），将所有的Y坐标集中起来，然后将其两两配对组成一个二维的网络。注意，这样的网格点之间的实际距离并不是均匀的，但是没有关系。

我们再来遍历所有的矩形：在这个网络中找到每个矩形所框起来的范围（遵循左闭右开的原则），标记这个范围内的网格点为true，意味着这些网格点是落在被cover的面积里。遍历完所有的矩形后，所有标记为true的网格点都是要被算入面积的，而那些没有标记的说明不用被计算。

可以知道，凡是被cover的网格点(u,v)，它所代表的面积就是(x[u+1]-x[u])*(y[v+1]-y[v])。我们只需要累加这些网格点所代表的面积即可。


这个思路是我觉得很好的一个

```python
class Solution:
    def rectangleArea(self, rectangles: 'List[List[int]]') -> 'int':
        setx, sety = set(), set()
        for rec in rectangles:
            setx.add(rec[0])
            setx.add(rec[2])
            sety.add(rec[1])
            sety.add(rec[3])
            
        x, y = sorted(list(setx)), sorted(list(sety))
        m, n = len(x), len(y)
        points = [[0] * n for _ in range(m)]
        for rec in rectangles:
            x0 = bisect.bisect_left(x, rec[0]) 
            x1 = bisect.bisect_left(x, rec[2]) 
            y0 = bisect.bisect_left(y, rec[1]) 
            y1 = bisect.bisect_left(y, rec[3]) 
            for i in range(x0, x1):
                for j in range(y0, y1):
                    points[i][j] = 1
                    
        res, mod = 0, 10 ** 9 + 7
        for i in range(m-1):
            for j in range(n-1):
                if points[i][j] == 0:
                    continue
                res += (x[i+1] - x[i]) * (y[j+1] - y[j])
                res %= mod
        return res
```



















