leetcode刷题记录-2013. 检测正方形-双重哈希表存储数据

604 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

今日的题目存储数据的方式用到了双重map表,对于二维的数据来说,可能二维数组也能实现存储,但是嵌套的map表会更加节省内存。

每日一题

今天的每日一题为 2013. 检测正方形,难度为中等
给你一个在 X-Y 平面上的点构成的数据流。设计一个满足下述要求的算法:

  • 添加 一个在数据流中的新点到某个数据结构中。可以添加 重复 的点,并会视作不同的点进行处理。

  • 给你一个查询点,请你从数据结构中选出三个点,使这三个点和查询点一同构成一个 面积为正 的 轴对齐正方形 ,统计 满足该要求的方案数目。

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

  • 实现 DetectSquares 类:

  • DetectSquares() 使用空数据结构初始化对象

  • void add(int[] point) 向数据结构添加一个新的点 point = [x, y]

  • int count(int[] point) 统计按上述方式与点 point = [x, y] 共同构造 轴对齐正方形 的方案数。   示例:

image.png

输入:
["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) {
    ...
  }
}

image.png