leetcode
leetcode 1751 ~ 1800
检测正方形

检测正方形

难度:

标签:

题目描述

给你一个在 X-Y 平面上的点构成的数据流。设计一个满足下述要求的算法:

  • 添加 一个在数据流中的新点到某个数据结构中可以添加 重复 的点,并会视作不同的点进行处理。
  • 给你一个查询点,请你从数据结构中选出三个点,使这三个点和查询点一同构成一个 面积为正轴对齐正方形统计 满足该要求的方案数目

轴对齐正方形 是一个正方形,除四条边长度相同外,还满足每条边都与 x-轴 或 y-轴 平行或垂直。

实现 DetectSquares 类:

  • DetectSquares() 使用空数据结构初始化对象
  • void add(int[] point) 向数据结构添加一个新的点 point = [x, y]
  • int count(int[] point) 统计按上述方式与点 point = [x, y] 共同构造 轴对齐正方形 的方案数。

 

示例:

输入:
["DetectSquares", "add", "add", "add", "count", "count", "add", "count"]
[[], [[3, 10]], [[11, 2]], [[3, 2]], [[11, 10]], [[14, 8]], [[11, 2]], [[11, 10]]]
输出:
[null, null, null, null, 1, 0, null, 2]

解释:
DetectSquares detectSquares = new DetectSquares();
detectSquares.add([3, 10]);
detectSquares.add([11, 2]);
detectSquares.add([3, 2]);
detectSquares.count([11, 10]); // 返回 1 。你可以选择:
                               //   - 第一个,第二个,和第三个点
detectSquares.count([14, 8]);  // 返回 0 。查询点无法与数据结构中的这些点构成正方形。
detectSquares.add([11, 2]);    // 允许添加重复的点。
detectSquares.count([11, 10]); // 返回 2 。你可以选择:
                               //   - 第一个,第二个,和第三个点
                               //   - 第一个,第三个,和第四个点

 

提示:

  • point.length == 2
  • 0 <= x, y <= 1000
  • 调用 addcount总次数 最多为 5000

代码结果

运行时间: 90 ms, 内存: 17.8 MB


// 思路:使用Java Stream流式处理来实现相同的逻辑,通过Map来记录每个点的数量,并利用流来实现add和count方法。

import java.util.*;
import java.util.stream.*;

public class DetectSquares {
    private Map<Integer, Map<Integer, Integer>> pointCount;

    public DetectSquares() {
        pointCount = new HashMap<>();
    }

    public void add(int[] point) {
        int x = point[0], y = point[1];
        pointCount.putIfAbsent(x, new HashMap<>());
        pointCount.get(x).put(y, pointCount.get(x).getOrDefault(y, 0) + 1);
    }

    public int count(int[] point) {
        int x1 = point[0], y1 = point[1];
        return pointCount.getOrDefault(x1, Collections.emptyMap()).entrySet().stream()
                .filter(e -> e.getKey() != y1)
                .flatMapToInt(e -> {
                    int y2 = e.getKey();
                    int sideLength = Math.abs(y1 - y2);
                    int count1 = pointCount.getOrDefault(x1 + sideLength, Collections.emptyMap()).getOrDefault(y1, 0) *
                            pointCount.getOrDefault(x1 + sideLength, Collections.emptyMap()).getOrDefault(y2, 0);
                    int count2 = pointCount.getOrDefault(x1 - sideLength, Collections.emptyMap()).getOrDefault(y1, 0) *
                            pointCount.getOrDefault(x1 - sideLength, Collections.emptyMap()).getOrDefault(y2, 0);
                    return IntStream.of(count1 * e.getValue(), count2 * e.getValue());
                })
                .sum();
    }
}

解释

方法:

本题解通过使用一个嵌套的字典结构来存储点的坐标和频率。外层字典的键是点的x坐标,而内层字典的键是y坐标,内层字典的值则是该点的出现频率。添加点时,只需简单增加该点的计数。在计数函数中,遍历与查询点x坐标相同的所有点,计算与查询点y坐标的距离(即边长),然后检查是否存在与查询点形成正方形的其他三个点。这种方法通过直接访问字典的键来验证点的存在性,避免了遍历整个数据结构的需要。

时间复杂度:

O(n) per query, where n is the number of points with the same x-coordinate as the query point

空间复杂度:

O(N) where N is the number of unique points in the data structure

代码细节讲解

🦆
在函数`count`中,你是如何确定遍历哪些点的?为什么只遍历与查询点x坐标相同的点?
在`count`函数中,目标是找到所有可能与查询点形成正方形的点的组合。因此,首先需要确定一条边的两个点,这两点的x坐标必须相同(即垂直边),这是因为正方形的边要么垂直要么水平。通过限定遍历所有与查询点x坐标相同的点,我们可以在这些点中查找可以与查询点形成垂直边的点。一旦确定了垂直边,正方形的剩余两个点位置也随之确定,这减少了需要遍历的点的数量,从而提高了效率。
🦆
在考虑正方形的两种可能的位置时,你如何保证这两种位置在平面上的实际存在性,特别是在边界条件如坐标为0或1000时?
在算法中,当考虑正方形的两种可能的位置(即正方形的一个边平行于x轴的两种对角线方向)时,必须检查形成正方形的其他两个点的坐标是否有效。这意味着在进行坐标计算后,我们需要验证这些计算出的坐标是否在有效范围内(例如在问题给定的坐标范围内,通常是0到1000)。在字典查找操作中,如果某个坐标不在字典中,则默认不存在该点,因此不会对不存在的点误认为存在。这种检查方式自然地处理了边界情况,因为任何超出边界的坐标都不会在字典中有对应的条目。
🦆
你的解决方案在检测点的存在性时直接访问字典,这种方法在哪些情况下可能会引发错误或效率问题?
直接通过字典访问点的存在性是一种高效且通常安全的方法,因为字典的查找时间复杂度为O(1)。然而,这种方法可能在以下情况引发问题:1) 如果在访问字典时未先检查键是否存在,可能会引发KeyError错误。在本题解中,这通过安全地检查键是否存在来避免(例如使用`in`操作符)。2) 在极端情况下,如果点的分布极其稀疏,大多数坐标位置都没有点,那么在这些空坐标上的字典访问将变得不必要且效率低下,尤其是当尝试寻找不存在的点组合形成正方形时。此外,如果数据非常庞大,字典所占用的内存也可能成为问题。

相关问题