2013. 检测正方形
题目描述
给你一个在 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 。你可以选择:
// - 第一个,第二个,和第三个点
// - 第一个,第三个,和第四个点
提示
- p o i n t . l e n g t h = = 2 point.length == 2 point.length==2
- 0 < = x , y < = 1000 0 <= x, y <= 1000 0<=x,y<=1000
- 调用
add
和count
的总次数
最多为5000
解题思路
本题使用哈希表
的思路解决。首先,考虑如何实现int count(int[] point)
,这里,将输入的point的横纵坐标分别记为x和y,则由该点构成的正方形的上下两条边中,其中一条边的纵坐标为
y
y
y。通过枚举另一条边的坐标为col
,则正方形的边长d为
∣
y
−
c
o
l
∣
|y-col|
∣y−col∣,且大于0。
有了其中一个点的坐标(x,y)和一条横边的纵坐标col,就可以得到正方形的四个点的坐标,分别是 ( x , y ) , ( x , c o l ) , ( x + d , y ) , ( x + d , c o l ) (x, y), (x, col), (x+d, y), (x+d, col) (x,y),(x,col),(x+d,y),(x+d,col)或者 ( x , y ) , ( x , c o l ) , ( x − d , y ) , ( x − d , c o l ) (x, y), (x, col), (x-d, y), (x-d, col) (x,y),(x,col),(x−d,y),(x−d,col)。
因此,构建一个哈希表
,用来存储void add(int[] point)函数新增的点。首先将点按照行来划分,键为行的纵坐标, 值为另一个哈希表(其中的键为该行中的点的横坐标,值为同样的点的个数)。由于可能出现新增重复的点,所以计算正方形的个数的时候需要把另外三个点出现的次数相乘。
代码
Python
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @FileName :DetectSquares.py
# @Time :2022/1/26 14:01
# @Author :PangXZ
# Leetcode 2013: 检测正方形
from collections import defaultdict
from typing import List, Counter
class DetectSquares:
def __init__(self):
self.map = defaultdict(Counter)
def add(self, point: List[int]) -> None:
x, y = point
self.map[y][x] += 1
def count(self, point: List[int]) -> int:
res = 0
x, y = point
if not y in self.map:
return 0
yCount = self.map[y]
for col, colCount in self.map.items():
if col != y:
# 根据对称性,这里可以不用取绝对值
d = col - y
res += colCount[x] * yCount[x + d] * colCount[x + d]
res += colCount[x] * yCount[x - d] * colCount[x - d]
return res
# Your DetectSquares object will be instantiated and called as such:
# obj = DetectSquares()
# obj.add(point)
# param_2 = obj.count(point)
Java
class DetectSquares {
// 创建嵌套的哈希表:{x, {y : 点 (x,y) 数量}}
Map<Integer, Map<Integer, Integer>> rowcolMap = new HashMap<>();
public void add(int[] point) {
int x = point[0], y = point[1];
Map<Integer, Integer> col2Count = rowcolMap.getOrDefault(x, new HashMap<>());
col2Count.put(y, col2Count.getOrDefault(y, 0) + 1);
rowcolMap.put(x, col2Count);
}
public int count(int[] point) {
int x = point[0], y = point[1];
int ans = 0;
// 首先查询x行有哪些列
Map<Integer, Integer> col2Count = rowcolMap.getOrDefault(x, new HashMap<>());
// 枚举存在的列,即枚举点(x,ny)
for(int ny: col2Count.keySet()){
if(ny == y) continue;
int c1 = col2Count.get(ny); // 获取(x, ny)点的数量
int len = y - ny; // 计算边的长度,无需考虑正负,因为后面已经分为两种情况了
int[] nums = new int[]{x+len, x-len}; // 将可能的两点的横坐标构成数组
// 遍历两种情况
for(int nx: nums){
// 获取指定x坐标的y坐标信息,包括点的个数
Map<Integer, Integer> temp = rowcolMap.getOrDefault(nx, new HashMap<>());
// c2 表示(x+-len, y) 的点的个数, c3表示(x+-len, ny)点的个数
int c2 = temp.getOrDefault(y, 0), c3 = temp.getOrDefault(ny, 0);
ans += c1 * c2 * c3;
}
}
return ans;
}
}
/**
* Your DetectSquares object will be instantiated and called as such:
* DetectSquares obj = new DetectSquares();
* obj.add(point);
* int param_2 = obj.count(point);
*/
复杂度分析
- 时间复杂度:add 操作的复杂度为 O(1),count 最坏情况会扫描完所有此前加入的点,复杂度为 O(n)
- 空间复杂度:O(n)