美文网首页算法提高之LeetCode刷题
850. 矩形面积II(Python)

850. 矩形面积II(Python)

作者: 玖月晴 | 来源:发表于2019-05-09 21:39 被阅读0次

    题目

    难度:★★★★☆

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

    矩形面积

    找出平面中所有矩形叠加覆盖后的总面积。 由于答案可能太大,请返回它对 10 ^ 9 + 7 取模的结果。

    提示:

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

    示例

    示例 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 。

    解答

    这道题很有意思,我们使用切菜(笔者自己编的名字)原理,这里借鉴了CSDN某大佬的思路:

    切菜原理
    1. 将所有方框纵坐标y取出来,融合,去重并排序得到一个m个数字的列表,把每个数值当成平行于横坐标的一把刀,用m把刀切开所有的矩形,每一个切好的小矩形落在这m-1个横长条区域中的一个;

    2. 对于每一个矩形,判断被哪一把刀切了,切完以后落在哪个区域,用一个字典统计最后每个长条区域中都有哪些小矩形框;

    3. 对重合的小矩形框进行去重,相当于对x轴上的若干线段进行融合;

    4. 累加计算每个区域的覆盖面积,并考虑数值越界等特殊情况。

    class Solution:
        def rectangleArea(self, rectangles):
            """
            假设本题目中每一个线段是用元组表示的,且元组中只有两个元素,第一个小于第二个。
            :param rectangles:
            :return:
            """
    
            def get_div_lines(rectangles):
                """
                输入矩形列表,取出所有矩形的上下边界组成新列表,去除重复值,返回排序后的分隔线的y坐标列表。
                :param rectangles:
                :return:
                """
                return sorted(list(set([rect[1] for rect in rectangles] + [rect[3] for rect in rectangles])))
    
            def init_range_dict(div_lines):
                """
                根据边界划分整个坐标系,函数返回一个字典,字典中存储了每一个y轴区域上有覆盖的矩形的x范围,可能有多个x范围
                1. 字典的键key是各个y轴的分段,每个分段用元组合表示;
                2. 字典的值是线段列表,表示处在key范围内的所有x轴上的有覆盖的区域的x坐标范围,每条线段用元组表示
                函数返回该字典
                :param div_lines:
                :return:
                """
                return {(div_lines[i], div_lines[i+1]): [] for i in range(len(div_lines)-1)}
    
            def cut_rectangle(range_dict, div_lines, rectangle):
                """
                输入y轴分隔线,将当前矩阵用y轴分隔线切成不同小段,把切好的小矩形的x左右坐标放在range_dict中相应的位置,即加入到相应列表中。
                :param range_dict: 范围字典,每切一块矩阵都会进行分配和给更新键值
                :param div_lines: 分隔线们,或者每把刀所在位置,都是横线
                :param rectangle: 当前要被切的一个矩形,切好了放在字典中相应的位置
                :return:
                """
                x1, y1, x2, y2 = rectangle
                assert x1 < x2 and y1 < y2, 'Input Error, Please examine your input format.'
                for i in range(div_lines.index(y1), div_lines.index(y2)):
                    range_dict[(div_lines[i], div_lines[i+1])].append((x1, x2))
                return range_dict
    
            def cut_rectangles(range_dict, div_lines, rectangles):
                """
                把所有矩形都进行切块和分配
                :param range_dict:
                :param div_lines:
                :param rectangles:
                :return:
                """
                for rect in rectangles:
                    range_dict = cut_rectangle(range_dict, div_lines, rect)
                return range_dict
    
            def combine_line_segmentes(seg_list):
                """
                x方向上很多线段,这些线段可能重合,可以归并,例如[[1,3], [2,6], [7,9]]就可以归并为[[1, 6], [7, 9]
                :param seg_list: 输入切好的x轴线段们,进行一个整理和归并
                :return: 
                """
                if len(seg_list) <= 1:
                    return seg_list
                sorted_list = [tp[1] for tp in sorted(zip([seg[0] for seg in seg_list], seg_list))]
                new_list = []
                new_line_x1, new_line_x2 = sorted_list[0]
                i = 1
                while True:
                    x1, x2 = sorted_list[i]
    
                    if new_line_x1 <= x1 <= new_line_x2:
                        new_line_x2 = max(new_line_x2, x2)
    
                    elif x1 > new_line_x2:
                        new_list.append((new_line_x1, new_line_x2))
                        new_line_x1, new_line_x2 = x1, x2
    
                    if i == len(seg_list) - 1:
                        new_list.append((new_line_x1, new_line_x2))
                        break
                    i += 1
    
                return new_list
    
            def calculate_area(range_dict):
                """
                整体计算
                :param range_dict: 
                :return: 
                """
                area = 0                                                    # 初始化面积
                for y_range, x_ranges in range_dict.items():                # 依次取出来字典中的键和值
                    y_len = y_range[1] - y_range[0]                         # 字典的键实际代表当前的横条带区域,相减得到条带宽
                    x_ranges = combine_line_segmentes(x_ranges)             # 字典的值是个x方向的线段列表,进行一下整理和归并
                    x_len = sum([line[1]-line[0] for line in x_ranges])     # 计算x方向上的所有线段长度
                    area += y_len * x_len                                   # 横纵方向上相乘即可得到落在当前横条带中小矩形的面积
                return area
    
            y_div_lines = get_div_lines(rectangles)                         # 获得每一把刀的位置
            range_dict = init_range_dict(y_div_lines)                       # 初始化菜篮子
            range_dict = cut_rectangles(range_dict, y_div_lines, rectangles)# 切菜,把每个矩形切好放在菜篮子覆中该放的位置
            area = calculate_area(range_dict)                               # 计算总的面积
            if area > 1000000007:                                           # 越界评判
                return area % 1000000007
            return area
    

    执行用时 : 92 ms, 在Rectangle Area II的Python3提交中击败了42.86% 的用户
    内存消耗 : 13 MB, 在Rectangle Area II的Python3提交中击败了100.00% 的用户

    如有疑问或建议,欢迎评论区留言~

    相关文章

      网友评论

        本文标题:850. 矩形面积II(Python)

        本文链接:https://www.haomeiwen.com/subject/pnxnoqtx.html