【1401. 圆和矩形是否有重叠】

来源:力扣(LeetCode)

描述:

给你一个以 (radius, xCenter, yCenter) 表示的圆和一个与坐标轴平行的矩形 (x1, y1, x2, y2) ,其中 (x1, y1) 是矩形左下角的坐标,而 (x2, y2) 是右上角的坐标。

如果圆和矩形有重叠的部分,请你返回 true ,否则返回 false 。

换句话说,请你检测是否 存在 点 (xi, yi) ,它既在圆上也在矩形上(两者都包括点落在边界上的情况)。

示例 1 :
1

输入:radius = 1, xCenter = 0, yCenter = 0, x1 = 1, y1 = -1, x2 = 3, y2 = 1
输出:true
解释:圆和矩形存在公共点 (1,0)

示例 2 :

输入:radius = 1, xCenter = 1, yCenter = 1, x1 = 1, y1 = -3, x2 = 2, y2 = -1
输出:false

示例 3 :

2

输入:radius = 1, xCenter = 0, yCenter = 0, x1 = -1, y1 = 0, x2 = 0, y2 = 1
输出:true

提示:

  • 1 <= radius <= 2000
  • -104 <= xCenter, yCenter <= 104
  • -104 <= x1 < x2 <= 104
  • -104 <= y1 < y2 <= 104

方法一:分区域讨论

思路

在思考这个问题之前,我们先考虑一种临界情况:什么时候圆和矩形只有一个公共点呢?有两种情况:

  • 圆「贴」在矩形的四周
  • 圆「顶」在矩形的顶点

设圆心的坐标为 (x, y),圆的半径为 r, 矩形的左下端点为 (x1, y1),右上端点为 (x2, y2),形式化地,这两种情况可以继续细分:

  • 圆「贴」在矩形的四周
    • x = x1 − r, y ∈ [y1 , y2]
    • x = x2 + r, y ∈ [y1 , y2]
    • y = y1 − r, x ∈ [x1 , x2]
    • y = y2 + r, x ∈ [x1 , x2]
  • 圆「顶」在矩形的顶点
    • (x − x1)2 + (y − y1)2 = r2 , x ∈ [x1 − r, x], y ∈ [y1 − r, y]
    • (x − x2)2 + (y − y1)2 = r2 , x ∈ [x2, x2 + r], y ∈ [y1 − r, y]
    • (x − x1)2 + (y − y2)2 = r2 , x ∈ [x1 − r, x], y ∈ [y2, y2 + r]
    • (x − x2)2 + (y − y2)2 = r2 , x ∈ [x2, x2 + 2], y ∈ [y2, y2 + r]

由此可见,圆心临界位置的轨迹是一个「圆角矩形」——如果我们尝试把圆心向「圆角矩形」内部移动,就一定会出现公共点;如果向「圆角矩形」外部移动,就不会出现公共点。那么问题就转化成了判断圆心是否在这个圆角矩形内,如果在就表示有公共点,否则没有公共点。

3

对于这个圆角矩形我们可以分成九个部分来讨论:

  • 圆心在中心矩形中:x ∈ [x1 , x2], y ∈ [y1 , y2]
  • 圆心在上部矩形中:x ∈ [x1 , x2], y ∈ [y1 , y2 + r]
  • 圆心在下部矩形中:x ∈ [x1 , x2], y ∈ [y1 - r , y2]
  • 圆心在左部矩形中:x ∈ [x1 - r , x2], y ∈ [y1 , y2]
  • 圆心在右部矩形中:x ∈ [x1 , x2 + r], y ∈ [y1 , y2]
  • 圆心在左下方圆角中:(x − x1)2 + (y − y1)2 ≤ r2 , x ∈ [x1 − r, x], y ∈ [y1 − r, y]
  • 圆心在右下方圆角中:(x − x2)2 + (y − y1)2 ≤ r2 , x ∈ [x2, x2 + r], y ∈ [y1 − r, y]
  • 圆心在左上方圆角中:(x − x1)2 + (y − y2)2 ≤ r2 , x ∈ [x1 − r, x], y ∈ [y2, y2 + r]
  • 圆心在右上方圆角中:(x − x2)2 + (y − y2)2 ≤ r2 , x ∈ [x2, x2 + 2], y ∈ [y2, y2 + r]

对于上述情况我们分别进行讨论,由于在判断圆角的情况时,已经判断过五种矩形内的情况,所以不需要再分别讨论横坐标和纵坐标的取值范围,直接判断圆心到顶点的距离即可。

代码:

class Solution {
public:
    long long distance(int ux, int uy, int vx, int vy) {
        return (long long)pow(ux - vx, 2) + (long long)pow(uy - vy, 2);
    }

    bool checkOverlap(int radius, int xCenter, int yCenter, int x1, int y1, int x2, int y2) {
        /* 圆心在矩形内部 */
        if (x1 <= xCenter && xCenter <= x2 && y1 <= yCenter && yCenter <= y2) {
            return true;
        }
        /* 圆心在矩形上部*/
        if (x1 <= xCenter && xCenter <= x2 && y2 <= yCenter && yCenter <= y2 + radius) {
            return true;
        }
        /* 圆心在矩形下部*/
        if (x1 <= xCenter && xCenter <= x2 && y1 - radius <= yCenter && yCenter <= y1) {
            return true;
        }
        /* 圆心在矩形左部*/
        if (x1 - radius <= xCenter && xCenter <= x1 && y1 <= yCenter && yCenter <= y2) {
            return true;
        }
        /* 圆心在矩形右部*/
        if (x2 <= xCenter && xCenter <= x2 + radius && y1 <= yCenter && yCenter <= y2) {
            return true;
        }
        /* 矩形左上角 */
        if (distance(xCenter, yCenter, x1, y2) <= radius * radius)  {
            return true;
        }
        /* 矩形左下角 */
        if (distance(xCenter, yCenter, x1, y1) <= radius * radius) {
            return true;
        }
        /* 矩形右上角 */
        if (distance(xCenter, yCenter, x2, y2) <= radius * radius) {
            return true;
        }
        /* 矩形右下角 */
        if (distance(xCenter, yCenter, x1, y2) <= radius * radius) {
            return true;
        }
        /* 无交点 */
        return false;
    }
};

执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:5.7 MB, 在所有 C++ 提交中击败了95.54%的用户
复杂度
时间复杂度:O(1)。
空间复杂度:O(1)。

方法二:求圆心到矩形区域的最短距离

思路

在求圆和直线的位置关系时,我们常常会计算圆心到直线的垂直线段的距离。这条垂直线段的距离小于半径的时候,就说明两者相交。更进一步地考虑,其实是因为这条垂直线段的长度已经是圆心到直线上任意点中最小的了,如果最小的线段长度比圆半径小,说明存在点在圆内。

我们可以类比这个思想,来计算求圆心到矩形区域的最短距离。

我们可以分解成两个问题,即圆心到区域 x1 ≤ x ≤ x2 的最小值 xmin ,和圆心到区域 y1 ≤ y ≤ y2 的最小值 ymin ,我们可以得到这样的关系:

4

圆心到矩形区域的最小距离就是 x m i n 2 + y m i n 2 \sqrt{x^2_{min} + y^2_{min}} xmin2+ymin2 。未了方便理解,我们可以把平面区域根据 x = x1 、x = x2 、y = y1 、y = y2 四条直线分割成九个区域,分类讨论就可以合并到这个结果。

得到这个距离之后,我们再和半径比较,如果这个距离不大于半径的话,就说明存在公共点。

代码:

class Solution {
public:
    bool checkOverlap(int radius, int xCenter, int yCenter, int x1, int y1, int x2, int y2) {
        long long dist = 0;
        if (xCenter < x1 || xCenter > x2) {
            dist += min(pow(x1 - xCenter, 2), pow(x2 - xCenter, 2));
        }
        if (yCenter < y1 || yCenter > y2) {
            dist += min(pow(y1 - yCenter, 2), pow(y2 - yCenter, 2));
        }
        return dist <= radius * radius;
    }
};

执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:5.6 MB, 在所有 C++ 提交中击败了99.01%的用户
复杂度
时间复杂度:O(1)。
空间复杂度:O(1)。
author:LeetCode-Solution

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千北@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值