leetcode
leetcode 1951 ~ 2000
统计圆内格点数目

统计圆内格点数目

难度:

标签:

题目描述

给你一个二维整数数组 circles ,其中 circles[i] = [xi, yi, ri] 表示网格上圆心为 (xi, yi) 且半径为 ri 的第 i 个圆,返回出现在 至少一个 圆内的 格点数目

注意:

  • 格点 是指整数坐标对应的点。
  • 圆周上的点 也被视为出现在圆内的点。

 

示例 1:

输入:circles = [[2,2,1]]
输出:5
解释:
给定的圆如上图所示。
出现在圆内的格点为 (1, 2)、(2, 1)、(2, 2)、(2, 3) 和 (3, 2),在图中用绿色标识。
像 (1, 1) 和 (1, 3) 这样用红色标识的点,并未出现在圆内。
因此,出现在至少一个圆内的格点数目是 5 。

示例 2:

输入:circles = [[2,2,2],[3,4,1]]
输出:16
解释:
给定的圆如上图所示。
共有 16 个格点出现在至少一个圆内。
其中部分点的坐标是 (0, 2)、(2, 0)、(2, 4)、(3, 2) 和 (4, 4) 。

 

提示:

  • 1 <= circles.length <= 200
  • circles[i].length == 3
  • 1 <= xi, yi <= 100
  • 1 <= ri <= min(xi, yi)

代码结果

运行时间: 53 ms, 内存: 16.4 MB


/*
 * 思路:
 * 1. 使用流的方式遍历每个圆,检查圆内的每个整数坐标点。
 * 2. 使用集合记录所有出现在至少一个圆内的格点。
 * 3. 返回集合的大小。
 */
import java.util.HashSet;
import java.util.Set;
import java.util.stream.IntStream;

public class Solution {
    public int countLatticePoints(int[][] circles) {
        Set<String> points = new HashSet<>();
        IntStream.range(0, circles.length).forEach(index -> {
            int x = circles[index][0];
            int y = circles[index][1];
            int r = circles[index][2];
            IntStream.rangeClosed(x - r, x + r).forEach(i -> {
                IntStream.rangeClosed(y - r, y + r).forEach(j -> {
                    if ((i - x) * (i - x) + (j - y) * (j - y) <= r * r) {
                        points.add(i + "," + j);
                    }
                });
            });
        });
        return points.size();
    }
}

解释

方法:

此题解采用了差分数组和积分图的思想来计算网格中的格点数。首先,确定一个足够大的矩形区域来包含所有的圆。然后,对于每个圆,计算其横跨的所有行,并在每行的起始和结束位置进行标记,使用差分的方法记录进入圆和离开圆的点。最后,通过累积这些差分值来确定每个点是否在至少一个圆内,进而统计总的格点数目。

时间复杂度:

O(n * r + X * Y)

空间复杂度:

O(X * Y)

代码细节讲解

🦆
在题解中提到使用差分数组和积分图的方法,这个方法与传统的遍历所有网格点相比有什么优势?
使用差分数组和积分图的方法可以显著提高算法效率。在传统的遍历所有网格点的方法中,我们需要对每个网格点检查其是否位于任一圆内,这在圆的数量或网格的规模很大时非常耗时。而差分数组使得我们只需对每个圆影响的区域边界进行操作,然后通过简单的一次遍历来构建整个区域的覆盖情况,从而降低了时间复杂度。这种方法特别适用于处理大规模数据,能够有效地减少计算量和提高性能。
🦆
如何确定题解中提到的'足够大的矩形区域'的具体大小,以确保包含所有的圆?
为了确定包含所有圆的矩形区域大小,我们首先需要找出所有圆的最远边界。具体来说,我们计算所有圆心加上其半径得到的最大x和最大y值(即每个圆的最右侧和最上侧位置),然后再分别在这些值的基础上增加一定的边界(例如加1或加2),以确保整个圆都被包含在内。这样计算出的矩形区域保证可以覆盖所有的输入圆。
🦆
题解中计算差分数组时,使用了`isqrt(cr * cr - (y - cy) ** 2)`来确定圆在某一行的横跨范围。此公式的正确性及其如何工作能否详细解释一下?
此公式是基于圆的方程(x - cx)^2 + (y - cy)^2 = cr^2来计算的。在确定圆在某一行y的横跨范围时,我们固定y值,解出x值。将圆的方程改写为x的形式,x = sqrt(cr^2 - (y - cy)^2) + cx。这里,isqrt是整数平方根函数,用于确保结果为整数,这对于网格点计算来说是必要的。计算出x的范围后,我们就得到了圆在该行的横跨范围,即从(cx - z)到(cx + z)。
🦆
在计算差分数组时,为什么在圆的右边界的下一个位置`cx + z + 1`处减1?
差分数组的工作原理是通过记录每个区间的开始和结束来快速计算区间内的累积值。当我们在`cx - z`处加1时,表示从这个点开始,圆开始覆盖这一段区域。而在`cx + z + 1`处减1的目的是表示圆在`cx + z`结束覆盖,因此需要在其后一个位置减去1,以确保只在圆的实际覆盖范围内计算覆盖次数。这样,当我们对差分数组进行累积求和时,每个被圆覆盖的点都会被正确计算,而圆外的点则不会被错误地包括。

相关问题