首页 > 其他分享 >leetcode 扫描线专题 06-leetcode.836 rectangle-overlap 力扣.836 矩形重叠

leetcode 扫描线专题 06-leetcode.836 rectangle-overlap 力扣.836 矩形重叠

时间:2024-11-18 13:40:19浏览次数:1  
标签:rec1 06 重叠 int leetcode.836 扫描线 rec2 矩形

题目

矩形以列表 [x1, y1, x2, y2] 的形式表示,其中 (x1, y1) 为左下角的坐标,(x2, y2) 是右上角的坐标。

矩形的上下边平行于 x 轴,左右边平行于 y 轴。

如果相交的面积为 正 ,则称两矩形重叠。

需要明确的是,只在角或边接触的两个矩形不构成重叠。

给出两个矩形 rec1 和 rec2。如果它们重叠,返回 true;否则,返回 false 。

示例 1:

输入:rec1 = [0,0,2,2], rec2 = [1,1,3,3]
输出:true

示例 2:

输入:rec1 = [0,0,1,1], rec2 = [1,0,2,1]
输出:false

示例 3:

输入:rec1 = [0,0,1,1], rec2 = [2,2,3,3]
输出:false

提示:

rect1.length == 4

rect2.length == 4

-10^9 <= rec1[i], rec2[i] <= 10^9

rec1 和 rec2 表示一个面积不为零的有效矩形

小结

感觉有一个顺序的问题。

应该先学习一下 T836 + T223 + T850 可能再做这一题就会比较自然。

v1-投影

思路

判断两个矩形是否重叠的通用条件是通过其投影区间是否有交集:

两矩形沿 x 轴投影有重叠,且两矩形沿 y 轴投影有重叠。

二维

矩形投影到坐标轴上,就变成了区间。稍加思考,我们发现:两个互相重叠的矩形,它们在 x 轴和 y 轴上投影出的区间也是互相重叠的。这样,我们就将矩形重叠问题转化成了区间重叠问题。

区间重叠是一维的问题,比二维问题简单很多。

我们可以穷举出两个区间所有可能的 6 种关系:

一维

可以看到,区间的 6 种关系中,不重叠只有两种情况,判断不重叠更简单。

假设两个区间分别是 [s1, e1] 和 [s2, e2] 的话,区间不重叠的两种情况就是 e1 <= s2 和 e2 <= s1。

重叠

公式化为:

x 轴投影相交:rec1[2] > rec2[0] && rec1[0] < rec2[2]
y 轴投影相交:rec1[3] > rec2[1] && rec1[1] < rec2[3]

解法

class Solution {
    public boolean isRectangleOverlap(int[] rec1, int[] rec2) {
        // 判断 x 轴和 y 轴投影是否有交集
        boolean xOverlap = rec1[2] > rec2[0] && rec1[0] < rec2[2];
        boolean yOverlap = rec1[3] > rec2[1] && rec1[1] < rec2[3];
        
        // 两个投影都有交集才算重叠
        return xOverlap && yOverlap;
    }
}

小结

投影这种解法看起来很简单,实际上很巧妙,但是有时候我们不见得能想到。

v2-重叠面积

思路

如果两个矩形重叠,那么重叠的面积一定大于0.

于是问题变成如何计算重叠的矩形面积?

二维

如果有重叠,重叠的部分也一定是一个矩形

交集面积的计算方式是基于两个矩形的重叠区域的 左下角和右上角的坐标 来推算的。

具体来说,两个矩形的交集矩形(如果存在)也是一个矩形,它的边界由两个矩形的边界决定。

计算重叠面积

  • 第一个矩形 rec1 = [x1, y1, x2, y2],表示其左下角坐标为 (x1, y1),右上角坐标为 (x2, y2)

  • 第二个矩形 rec2 = [x3, y3, x4, y4],表示其左下角坐标为 (x3, y3),右上角坐标为 (x4, y4)

如果两个矩形有交集,那么交集的矩形的左下角和右上角的坐标可以通过取两者的最大和最小值来确定:

  • 交集矩形的左下角(x1', y1') = (max(x1, x3), max(y1, y3))

  • 交集矩形的右上角(x2', y2') = (min(x2, x4), min(y2, y4))

判断交集是否存在

交集矩形的面积只有在其宽度和高度都大于 0 时才存在。

如果交集矩形的宽度或高度小于等于 0,说明两个矩形没有交集。

因此,交集矩形的宽度和高度的计算方式如下:

  • 宽度:width = x2' - x1'
  • 高度:height = y2' - y1'

交集矩形的面积则为:

  • 面积 = width * height,但是如果宽度或高度小于等于 0,面积就为 0。

因此,计算交集的面积时,我们需要确保 width > 0height > 0,否则交集的面积为 0。

代码示例

public class Solution {
    public boolean isRectangleOverlap(int[] rec1, int[] rec2) {
        // 计算交集矩形的左下角和右上角
        int x1 = Math.max(rec1[0], rec2[0]);
        int y1 = Math.max(rec1[1], rec2[1]);
        int x2 = Math.min(rec1[2], rec2[2]);
        int y2 = Math.min(rec1[3], rec2[3]);
        
        // 计算交集的宽度和高度
        int width = x2 - x1;
        int height = y2 - y1;
        
        // 如果交集的宽度和高度都大于 0,说明有重叠
        if (width > 0 && height > 0) {
            return true;
        }
        
        // 没有重叠
        return false;
    }
}

效果

0ms 100%

小结

这个感觉比方法 1 的投影应该更容易想到一些。

而且可以在后续的 T223 T3047 中拓展使用。

v3-扫描线

扫描线

使用扫描线(sweepline)方法解决这个问题也是一种非常常见且高效的方式。

扫描线的基本思路是将矩形视为一系列的 线段,并在 x 轴方向上依次“扫描”这些线段,检查是否有重叠。

思路

  1. 将矩形的边界转换为事件

    • 每个矩形有 两条边,一个是左边界(x1),一个是右边界(x2)。这两条边界构成了扫描线的“事件”。
    • 对于每个矩形,生成两个事件:
      • 左边界事件:表示矩形开始时,增加一个 y 区间(从 y1y2)。
      • 右边界事件:表示矩形结束时,移除一个 y 区间(从 y1y2)。
  2. 事件排序

    • 所有的事件按 x 坐标排序。如果 x 坐标相同,优先处理右边界事件(因为在同一位置时,应该先移除结束的矩形,再处理新的开始)。
  3. 扫描线处理

    • 扫描线从左到右扫描这些事件。当扫描到一个 左边界事件 时,添加相应的 y 区间;当扫描到一个 右边界事件 时,移除相应的 y 区间。
    • 在每次添加或移除事件时,检查当前的 y 区间是否存在重叠。如果存在重叠,那么矩形就重叠。
  4. 区间重叠检查

    • 在扫描线过程中,维护一个 活动的 y 区间集合,该集合记录了当前所有矩形在 y 轴上的区间。
    • 每次添加新的 y 区间时,检查当前 y 区间是否与已经存在的区间重叠。如果重叠,则说明存在矩形重叠。

详细步骤

  1. 定义事件

    • 每个矩形 rec = [x1, y1, x2, y2] 会生成两个事件:
      • 左边界事件 (x1, y1, y2, 1) 表示矩形开始(1 表示是左边界)。
      • 右边界事件 (x2, y1, y2, -1) 表示矩形结束(-1 表示是右边界)。
  2. 排序事件

    • 按照 x 坐标排序,如果两个事件的 x 坐标相同,右边界事件优先。

为什么要这样做?

因为在扫描线的过程中,当我们遇到同一个 x 坐标时,我们希望先处理右边界事件,再处理左边界事件。

这是为了避免在处理过程中出现误判的情况。

比如,当一个矩形的右边界和另一个矩形的左边界在同一个 x 坐标上时,我们应该先“移除”第一个矩形的 y 区间,然后再“添加”第二个矩形的 y 区间。

  1. 扫描并更新活动区间
    • 用一个数据结构(比如 TreeSet)维护当前的活动区间。每次扫描到一个事件时,更新该区间,检查是否存在重叠。

如何判断是否重叠:

private boolean hasOverlap(TreeSet<int[]> activeIntervals) {
    int prevEnd = Integer.MIN_VALUE;  // 初始化 prevEnd 为一个极小值
    for (int[] interval : activeIntervals) {
        // 如果当前区间的起始点小于前一个区间的终点,说明有重叠
        if (interval[0] < prevEnd) {
            return true;  // 有重叠,返回 true
        }
        prevEnd = interval[1];  // 更新 prevEnd 为当前区间的终点
    }
    return false;  // 如果没有任何重叠,返回 false
}

实现

import java.util.*;

public class Solution {
    public boolean isRectangleOverlap(int[] rec1, int[] rec2) {
        // 如果 rec1 和 rec2 的 x 范围没有交集,直接返回 false
        if (rec1[2] <= rec2[0] || rec1[0] >= rec2[2]) {
            return false;
        }

        // 生成事件列表
        List<int[]> events = new ArrayList<>();
        
        // 对 rec1 和 rec2 的边界生成事件
        // 左边界事件 [x, y1, y2, type],type = 1 表示左边界,-1 表示右边界
        events.add(new int[]{rec1[0], rec1[1], rec1[3], 1});  // rec1 左边界
        events.add(new int[]{rec1[2], rec1[1], rec1[3], -1}); // rec1 右边界
        events.add(new int[]{rec2[0], rec2[1], rec2[3], 1});  // rec2 左边界
        events.add(new int[]{rec2[2], rec2[1], rec2[3], -1}); // rec2 右边界

        // 按 x 坐标排序,x 相同的话,优先处理右边界事件
        events.sort((a, b) -> a[0] == b[0] ? b[3] - a[3] : a[0] - b[0]);
        
        // 活动区间维护
        TreeSet<int[]> activeIntervals = new TreeSet<>((a, b) -> a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]);

        for (int[] event : events) {
            int x = event[0];
            int y1 = event[1];
            int y2 = event[2];
            int type = event[3];

            if (type == 1) { // 左边界事件,加入活动区间
                activeIntervals.add(new int[]{y1, y2});
            } else { // 右边界事件,移除活动区间
                activeIntervals.remove(new int[]{y1, y2});
            }

            // 检查活动区间是否有重叠
            if (hasOverlap(activeIntervals)) {
                return true;
            }
        }
        
        return false;
    }

    // 检查活动区间是否有重叠
    private boolean hasOverlap(TreeSet<int[]> activeIntervals) {
        int prevEnd = Integer.MIN_VALUE;
        for (int[] interval : activeIntervals) {
            if (interval[0] < prevEnd) {
                return true;  // 有重叠
            }
            prevEnd = interval[1]; // 更新 prevEnd
        }
        return false;  // 没有重叠
    }
}

效果

1ms 击败2.08%

小结

整体上而言,我比较喜欢重叠面积的方式,这种比较好想到,而且可以扩展。

当然扫描线也是一种很通用的解法。

这一题的巧思属于维度投影的解法,很巧妙。

开源地址

为了便于大家学习,所有实现均已开源。欢迎 fork + star~

https://github.com/houbb/leetcode

扫描线专题

leetcode 扫描线专题 06-扫描线算法(Sweep Line Algorithm)

leetcode 扫描线专题 06-leetcode.218 the-skyline-problem 力扣.218 天际线问题

leetcode 扫描线专题 06-leetcode.252 meeting room 力扣.252 会议室

leetcode 扫描线专题 06-leetcode.253 meeting room ii 力扣.253 会议室 II

leetcode 扫描线专题 06-leetcode.1851 minimum-interval-to-include-each-query 力扣.1851 包含每个查询的最小区间

leetcode 扫描线专题 06-leetcode.223 rectangle-area 力扣.223 矩形面积

leetcode 扫描线专题 06-leetcode.3047 find-the-largest-area-of-square-inside-two-rectangles 力扣.3047 求交集区域的最大正方形面积

leetcode 扫描线专题 06-leetcode.391 perfect-rectangle 力扣.391 完美矩形

leetcode 扫描线专题 06-leetcode.836 rectangle-overlap 力扣.836 矩形重叠

leetcode 扫描线专题 06-leetcode.850 rectangle-area 力扣.850 矩形面积 II

参考资料

https://leetcode.cn/problems/4sum/

https://leetcode.cn/problems/rectangle-overlap/solutions/155825/tu-jie-jiang-ju-xing-zhong-die-wen-ti-zhuan-hua-we/

标签:rec1,06,重叠,int,leetcode.836,扫描线,rec2,矩形
From: https://www.cnblogs.com/houbbBlogs/p/18552426

相关文章

  • leetcode 扫描线专题 06-leetcode.391 perfect-rectangle 力扣.391 完美矩形
    题目给你一个数组rectangles,其中rectangles[i]=[xi,yi,ai,bi]表示一个坐标轴平行的矩形。这个矩形的左下顶点是(xi,yi),右上顶点是(ai,bi)。如果所有矩形一起精确覆盖了某个矩形区域,则返回true;否则,返回false。示例1:输入:rectangles=[[1,1,3,3],[3,1,4,2],......
  • 2024-2025-1 20241406 刘书含《计算机基础与程序设计》第8周学习总结
    2024-2025-120241406《计算机基础与程序设计》第8周学习总结这个作业属于哪个课程 2024-2025-1-计算机基础与程序设计这个作业要求在哪里 如2024-2025-1计算机基础与程序设计第八周作业这个作业的目标 功能设计与面向对象设计>面向对象设计过程面向对象语言三要素汇编、编......
  • 学期2024-2025-1 学号20241306 《计算机基础与程序设计》第8周学习总结
    学期2024-2025-1学号20241306《计算机基础与程序设计》第8周学习总结作业信息这个作业属于哪个课程<班级的链接>(如2024-2025-1-计算机基础与程序设计)这个作业要求在哪里<作业要求的链接>(如2024-2025-1计算机基础与程序设计第8周作业)这个作业的目标功能设计与面......
  • 20222306 2024-2025-1《网络与系统攻防技术》实验六实验报告
    1.实验内容1.1内容回顾总结这周都重点在于Metasploit工具的使用,我深入了解了对其功能和使用流程。Metasploit是一个功能强大的渗透测试框架,广泛应用于网络安全领域。它为安全专家、渗透测试人员和红队提供了一个全面的工具集,支持漏洞利用、攻击模拟和安全评估。Metasploit提......
  • 20222406 2024-2025-1 《网络与系统攻防技术》实验六实验报告
    202224062024-2025-1《网络与系统攻防技术》实验六实验报告1.实验内容前期渗透:在root权限下进行主机发现、端口扫描、扫系统版本和漏洞操作。多种漏洞利用:包括Vsftpd源码包后门漏洞(21端口)、SambaMS-RPCShell命令注入漏洞(139端口)、JavaRMISERVER命令执行漏洞(109......
  • 20222406 2024-2025-1 《网络与系统攻防技术》实验五实验报告
    202224062024-2025-1《网络与系统攻防技术》实验五实验报告1.实验内容对网站进行DNS域名查询,包括注册人、IP地址等信息,还通过相关命令查询IP地址注册人及地理位置。尝试获取QQ好友IP地址并查询其地理位置。使用nmap对靶机环境扫描,获取靶机IP活跃状态、开......
  • 20222306 2024-2025-1 《网络与系统攻防技术》实验五实验报告
    1.实验内容(1)从www.besti.edu.cn、baidu.com、sina.com.cn中选择一个DNS域名进行查询,获取如下信息:DNS注册人及联系方式该域名对应IP地址IP地址注册人及联系方式IP地址所在国家、城市和具体地理位置PS:使用whois、dig、nslookup、traceroute、以及各类在线和离线工具进行搜集......
  • (免费源码)计算机毕业设计必看必学 原创程序 java、PHP、python、文案全套、毕设成品等
    摘要由于数据库和数据仓库技术的快速发展,停车场管理系统建设越来越向模块化、智能化、自我服务和管理科学化的方向发展。停车场管理系统对处理对象和服务对象,自身的系统结构,处理能力,都将适应技术发展的要求发生重大的变化。停车场管理系统除了具有共享系统的全部功能以外,能......
  • 报错:ORA-00603、ORA-01092、ORA-00704, ORA-00604, ORA-00904
    基本情况在做备份还原的时候,可能是第三方备份软件的配置的原因,使得我在测试服务器做恢复测试时,使用的备份集不是我的预期的备份集。我想恢复的是19c的数据库,而我实际恢复的是一个11g的数据库。我在恢复控制文件、数据文件和归档日志文件的时候都很顺利,recoverdatabase也成功,但......
  • leetcode 扫描线专题 06-leetcode.252 meeting room 力扣.252 会议室
    扫描线专题leetcode扫描线专题06-扫描线算法(SweepLineAlgorithm)leetcode扫描线专题06-leetcode.218the-skyline-problem力扣.218天际线问题leetcode扫描线专题06-leetcode.252meetingroom力扣.252会议室leetcode扫描线专题06-leetcode.253meetingroomii......