本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前言
今日的题目存储数据的方式用到了双重map表,对于二维的数据来说,可能二维数组也能实现存储,但是嵌套的map表会更加节省内存。
每日一题
今天的每日一题为 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 。你可以选择:
// - 第一个,第二个,和第三个点
// - 第一个,第三个,和第四个点
提示:
- point.length == 2
- 0 <= x, y <= 1000
- 调用 add 和 count 的 总次数 最多为 5000
题解
数据存储结构
这道题的关键点在于,要怎么存储我们的数据,因为add的点是会随机出现的,并且每一个点位可以重复出现多次。最开始考虑使用二维数组来保存数据,最后还是放弃了,写法不太优雅。
后来发现可以用哈希表 套 哈希表的方式,以 {x, {y : 点 (x,y) 数量}} 的形式对传入点进行存储。这样,我们第一个哈希表key为x点坐标,它的value是第二个哈希表,然后我们在通过第二个哈希表的key存储y坐标,通过value存储出现的次数。
很明显,哈希表的方式要优于二维数组,特别是当数据量比较巨大的时候
add添加方法
定义好数据结构之后就很简单了,题目的add方法会传入一个数组,其中x为第一位,y为第二位,我们只需要判断y为key的哈希是否存在,存在则x为key的哈希对应的value+1,不存在则在这个y为key的哈希上新建一个哈希表。
add(point) {
const x = point[0],
y = point[1];
if (!this.map.has(y)) {
this.map.set(y, new Map());
}
const yMap = this.map.get(y);
yMap.set(x, (yMap.get(x) || 0) + 1);
}
count计算方法
在count方法中,假设传入点(x,y),我们需要先去查询是否有y这个哈希表,不存在则直接返回0。存在则枚举出一条边的全部哈希表并且拿到点与点之间的距离为col,这样我们就能求出边长为 Math.abs(col - y) 知道了边长,和题目给的那个点,我们就可以带入哈希表中去查找各个点出现的次数,因为点是可以重复出现的,所以最后我们出现的次数要相乘。
count(point) {
let sum = 0;
let x = point[0],
y = point[1];
if (!this.map.has(y)) {
return 0;
}
const yMap = this.map.get(y);
const entries = this.map.entries();
for (const [xx, xxMap] of entries) {
if (xx !== y) {
// 根据对称性,这里可以不用取绝对值
let d = Math.abs(xx - y);
sum +=
(xxMap.get(x) || 0) *
(yMap.get(x + d) || 0) *
(xxMap.get(x + d) || 0);
sum +=
(xxMap.get(x) || 0) *
(yMap.get(x - d) || 0) *
(xxMap.get(x - d) || 0);
}
}
return sum;
}
总结方法
最后我们只需要凑好两个方法,用一个类其描述这道题目,在初始化的时候先初始化一个哈希表 this.map = new Map(); 之后就执行add和count就好了。
class DetectSquares {
constructor() {
this.map = new Map();
}
add(point) {
...
}
count(point) {
...
}
}