2013. 检测正方形

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

 思路:本题需要构建一个适合题意的数据结构。

先看第一个需求:添加 一个在数据流中的新点到某个数据结构中可以添加 重复 的点,并会视作不同的点进行处理

从数据范围为看,0 <= x, y <= 1000,我们是可以选择开一个int[1000 + 5][1000 + 5]的数组的。

但先别急,我们来看第二个需求:给你一个查询点,请你从数据结构中选出三个点,使这三个点和查询点一同构成一个 面积为正 的 轴对齐正方形 ,统计 满足该要求的方案数目

我们要怎么解决这个需求呢?想要确定一个正方形

A.可以选择确定边长和边的方向(上下左右[因为本题要求正方形的边平行于X-Y轴]),再加上一个给定点即可。

这种方案下,用int[1000 + 5][1000 + 5]来标记int[j][j]位置的点的个数是很方便的,因为可以利用给定的点和边长直接计算出来顶点的位置。然而麻烦的地方在于,边长是需要枚举的,同时还有左上左下右上右下四个位置的枚举,总的枚举复杂度为O(1000*4)。

class DetectSquares {
public:

    int points_num[1000 + 5][1000 + 5];

    DetectSquares() {
        memset(points_num, 0, sizeof(points_num));
    }
    
    void add(vector<int> point) {
        ++ points_num[point[0]][point[1]];
    }
    
    int count(vector<int> point) {
        int square_len, lx, ly, rx, ry, square_num = 0, dir;
        for(square_len = 1; square_len <= 1000; ++ square_len){
            for(dir = 0; dir < 4; ++ dir){
                switch(dir){
                    case 0://0.向左下,即当前点作为右上顶点
                            rx = point[0]; ry = point[1];
                            lx = rx - square_len; ly = ry - square_len;
                            if(lx >= 0 && ly >= 0){
                                square_num += points_num[lx][ly] * points_num[lx][ry] * points_num[rx][ly];
                            }
                            break;
                    case 1://1.向左上,即当前点作为右下顶点
                            rx = point[0]; ly = point[1];
                            lx = rx - square_len; ry = ly + square_len;
                            if(lx >= 0 && ry <= 1000){
                                square_num += points_num[lx][ly] * points_num[rx][ry] * points_num[lx][ry];
                            }
                            break;
                    case 2://2.向右下,即当前点作为左上顶点
                            lx = point[0]; ry = point[1];
                            rx = lx + square_len; ly = ry - square_len;
                            if(rx <= 1000 && ly >= 0){
                                square_num += points_num[lx][ly] * points_num[rx][ry] * points_num[rx][ly];
                            }
                            break;
                    case 3: //3.向右上,即当前点为左下顶点
                            lx = point[0]; ly = point[1];
                            rx = lx + square_len; ry = ly + square_len;
                            if(rx <= 1000 && ry <= 1000){
                                square_num += points_num[rx][ry] * points_num[lx][ry] * points_num[rx][ly];
                            }
                            break;
                    default:break;
                }
            }
        }
        return square_num;
    }
};

[注]:1)上述代码中rx,ry代表的是右上角的x、y坐标,lx,ly是左下角的x、y坐标。实际上我们可以有别的方式将代码进行统一,但是这样会失去可读性,也不容易排查bug。

         2)使用乘法做结果累计是因为题目中已经明说,同一个位置上的多个点算做不同的点,因此适用于组合问题的乘法规则。

         3)本题使用int[][]数组存储点信息,反而造成了枚举边长时的困难。

B.可以在与给定点同行或者同列的位置上选择一个点(计算该点与给定点之间的距离就得到了边长)+边的方向(本题已经说明构成的正方形的两边分别平行于X-Y轴,并且count函数会给出一个点,那么只需要额外再确定2个点就可以确定下正方形),

转换思路-由于本题中已经明说

  • 0 <= x, y <= 1000
  • 调用 add 和 count 的 总次数 最多为 5000

也就是说,在我们这个1000*1000的空间上,点的分布离散程度很大,此时在存储时我们就没有必要开这么大的数据,可以使用嵌套哈希的结构。

用martix_rows[i]来表示第i行,martix_rows[i][j]就等于上面的int[i][j],但由于点是离散的,因此martix_rows[i]中可能并没有存储0-1000列。

算法调整如下:给定点 (x,y)

我们只需要枚举martix_rows[x]中的所有元素,针对每一个元素都可以得到一个边长,此时按照上面的逻辑来即可。

这个思路可以利用数据离散分布的情况降低枚举边长的时间耗费。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值