难度:中等
目录
一、问题描述
这里直接采用的是LeetCode上面的问题描述。
给你一个在 X-Y 平面上的点构成的数据流。设计一个满足下述要求的算法:
- 添加 一个在数据流中的新点到某个数据结构中。可以添加 重复 的点,并会视作不同的点进行处理。
- 给你一个查询点,请你从数据结构中选出三个点,使这三个点和查询点一同构成一个 面积为正 的 轴对齐正方形 ,统计 满足该要求的方案数目。
轴对齐正方形 是一个正方形,除四条边长度相同外,还满足每条边都与 x-轴 或 y-轴 平行或垂直。
实现 DetectSquares
类:
- DetectSquares() 使用空数据结构初始化对象
- void add(int[] point) 向数据结构添加一个新的点 point = [x, y]
- int count(int[] point) 统计按上述方式与点 point = [x, y] 共同构造 轴对齐正方形 的方案数。
下面给出示例:
提示:
point.length == 2
0 <= x, y <= 1000
- 调用
add
和count
的 总次数 最多为5000
二、解题思想
轴对齐正方形 由两条对齐横轴的边,以及两条对其纵轴的边组成。可以使用 嵌套哈希表 表示 一条纵轴上所有的点和个数,或者一条横轴上所有的点的个数。
我这里用的是 将 x 存为 外层哈希表 的key ,内层哈希表 存放的是 y 的key 。 (当然,你也可以和我反过来也一样。)
以外层 key 来索引一条 纵轴 上的点,外层 key + 内层key 来确定该纵轴上的点 ,内层value 则是 点(外层 key , 内层key) 的个数。
这里介绍一下嵌套的哈希表:
unordered_map<int,unordered_map<int,int>> addPointer;
例如点:
- ( 4,5 )、( 4,6 )、( 4,7 )、( 4,7 )
- ( 6,6 )、( 6,6 )、( 6,7 )
- ( 7,5 )、( 7,6 )
在图上表现为:
抽象成哈希表,表现为:
有了以上哈希表后,我们便可以进行操作了:
围绕着点 point 分别有四个方位可以构成 轴对齐正方形:分别是 左上、左下、右上、右下。
方法如下:
- 假设输入的 point 为 [x,y];
- 那么可以根据哈希表确定 与 x 在一条纵轴上的点有几个,将其保存下来。
- 依次遍历哈希表中其他纵轴上所有的节点 ,此时可以由 x 与 纵轴 计算出 正方形的边长 d。
- 有了两条纵轴以及边长 d 和 起始点 ( x,y ) 之后可以确定正方形的 其他三个点 分别为、、
- 确定符合条件的点数,缺任意一个点都组不成 轴对齐正方形 所以可以利用乘法,将点数个数相乘来计算可以组成多少个 轴对齐正方形。
三、解题:
1、判断极端情况
这里开始可以判断 哈希表 内,是否有与 point.x 在同一纵轴上的点,如果没有的话是不能组成 轴对齐正方形 的,直接 return 0;
2、代码实现
class DetectSquares {
public:
DetectSquares() {
}
void add(vector<int> point) {
int x = point[0], y = point[1];
addPointer[x][y]++;
}
int count(vector<int> point) {
int x = point[0], y = point[1];
// 没有点与x在同一纵轴上,不能组成 轴对齐正方形
if(!addPointer.count(x)){
return 0;
}
//保存 可生成 正方形的个数
int ans = 0;
//保存 纵轴 x 上的所有点
unordered_map<int, int> xPointer = addPointer[x];
//it.first add过的x坐标 it.second add过的y坐标及其个数
for(auto it : addPointer){
//it 纵轴为it 的所有的点,如果it 与 x在一条纵轴上 那么不能组成 正方形 ,因为边长为0
if(it.first != x){
//由两条纵轴,确定正方形的边长
int d = x - it.first;
//1、索引 在纵轴 it.first 上,有没有点 满足(it.first,y),有的话,返回点的个数
//2、索引 在纵轴 x 上,有没有点 满足(x,y+d),有的话,返回点的个数
//3、索引 在纵轴 it.first 上,有没有点满足(it.first,y+d),有的话,返回点的个数
ans += (it.second.count(y) ? it.second[y] : 0) * (xPointer.count(y+d) ? xPointer[y+d] : 0 ) *
(it.second.count(y+d) ? it.second[y+d] : 0);
//上下索引的方向相反
ans += (it.second.count(y) ? it.second[y] : 0) * (xPointer.count(y-d) ? xPointer[y-d] : 0 ) *
(it.second.count(y-d) ? it.second[y-d] : 0);
}
}
return ans;
}
private:
unordered_map<int,unordered_map<int,int>> addPointer;
};
/**
* Your DetectSquares object will be instantiated and called as such:
* DetectSquares* obj = new DetectSquares();
* obj->add(point);
* int param_2 = obj->count(point);
*/
3、时间复杂度以及空间复杂度
时间复杂度: ( n 为哈希表中纵轴的条数)
空间复杂度:
四、总结
做了这一题,开始使用循环来暴力破解的,时间复杂度为。后面看了其他解题思路,竟然可以做到时间复杂度为 ,着实令我吃惊,刚开始没有整明白,然后自己一步步推到出过程,希望对你有帮助。
学会了哈希表的嵌套使用。
如果对你有什么帮助,请star ♥ 一下,收藏一下,蟹蟹啦!👇👇