【C/C++】6043. 统计包含每个点的矩形数目

860 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第30天,点击查看活动详情


题目链接:6043. 统计包含每个点的矩形数目

题目描述

给你一个二维整数数组 rectangles ,其中 rectangles[i] = [li, hi] 表示第 i 个矩形长为 lil_i 高为 hih_i 。给你一个二维整数数组 points ,其中 points[j] = [xj, yj] 是坐标为 (xj, yj) 的一个点。

第 i 个矩形的 左下角 在 (0, 0) 处,右上角 在 (li,hi)(l_i, h_i) 。

请你返回一个整数数组 count ,长度为 points.length,其中 count[j]包含 第 j 个点的矩形数目。

如果 0xjli0 \leqslant xj \leqslant li 且 0yjhi0 \leqslant yj \leqslant hi ,那么我们说第 i 个矩形包含第 j 个点。如果一个点刚好在矩形的 边上 ,这个点也被视为被矩形包含。

提示:

  • 1rectangles.length,points.length51041 \leqslant rectangles.length, points.length \leqslant 5 * 10^4
  • rectangles[i].length==points[j].length==2rectangles[i].length == points[j].length == 2
  • 1li,xj1091 \leqslant l_i, x_j \leqslant 10^9
  • 1hi,yj1001 \leqslant h_i, y_j \leqslant 100
  • 所有 rectangles 互不相同 。
  • 所有 points 互不相同 。

示例 1:

example1.png

输入:rectangles = [[1,2],[2,3],[2,5]], points = [[2,1],[1,4]]
输出:[2,1]
解释:
第一个矩形不包含任何点。
第二个矩形只包含一个点 (2, 1) 。
第三个矩形包含点 (2, 1) 和 (1, 4) 。
包含点 (2, 1) 的矩形数目为 2 。
包含点 (1, 4) 的矩形数目为 1 。
所以,我们返回 [2, 1]

示例 2:

example2.png

输入:rectangles = [[1,1],[2,2],[3,3]], points = [[1,3],[1,1]]
输出:[1,3]
解释:
第一个矩形只包含点 (1, 1) 。
第二个矩形只包含点 (1, 1) 。
第三个矩形包含点 (1, 3) 和 (1, 1) 。
包含点 (1, 3) 的矩形数目为 1 。
包含点 (1, 1) 的矩形数目为 3 。
所以,我们返回 [1, 3]

整理题意

题目给了一组数据 rectangles 表示矩形右上角的点(默认矩形左下角的点在 [0, 0]),又给了一组数据 points 表示点,问给定的 points 中每个点分别被几个矩形所覆盖。

解题思路分析

习惯性动作,首先观察题目数据:

  • 矩形和点的最大个数为 5e4
  • x 坐标最大 1e9y 坐标最大 100 很明显 y 坐标的数据范围较小可以作为我们解题的突破口。

由于 y 坐标最大为 100,我们对 rectangles 矩形右上角的点数据按照 y 坐标进行分类,查询某个点 points[i] 被多少个矩形覆盖时,我们只需要遍历大于等于该点 y 坐标的所有矩形(最多 100 个),统计同时大于等于该点 x 坐标的所有矩形即可。

这里可以提前将所有 y 坐标相同的矩形对 x 坐标进行排序,这样我们在统计大于等于 x 坐标的矩形时可以使用二分查找进行快速判断。

具体实现

  1. 对矩形右上角的点数据 rectangles 进行分类处理,将相同 y 坐标的点放入一组数据中,这里可以使用 map<int, vector<int>> mp; 进行存放(mp[y] = {x, ...}),也可以直接开一个二维矢量数组 vector<int> y[101]; 进行存放(y[y] = {x, ...}),因为 y 最大为 100 所以需要开 101 个空间。
  2. 遍历 points ,每次遍历大于等于该点 y 坐标的所有矩形,同时二分查找并统计大于等于该点 x 坐标的矩形个数。

复杂度分析

  • 时间复杂度:O((n+100m)logn)O((n + 100 * m)\log n)n 为矩形的个数,m 为点的个数。
  • 空间复杂度:O(n)O(n),仅需存放 n 个矩形右上角点的空间,不计返回值的空间。

代码实现

class Solution {
public:
    vector<int> countRectangles(vector<vector<int>>& rectangles, vector<vector<int>>& points) {
        //统计右上角点相同y坐标的矩形
        vector<int> y[101];
        for(int i = 0; i < 101; i++) y[i].clear();
        int n = rectangles.size();
        for(int i = 0; i < n; i++){
            y[rectangles[i][1]].push_back(rectangles[i][0]);
        }
        //相同y坐标的矩形对x坐标进行排序
        for(int i = 0; i < 101; i++){
            if(y[i].size() > 1) sort(y[i].begin(), y[i].end());
        }
        //遍历点坐标,并统计答案
        n = points.size();
        vector<int> ans(n, 0);
        for(int i = 0; i < n; i++){
            //遍历所有大于等于y坐标的矩形最多100
            for(int j = points[i][1]; j < 101; j++){
                //在确保大于等于y坐标的同时也确保大于等于x坐标
                ans[i] += y[j].end() - lower_bound(y[j].begin(), y[j].end(), points[i][0]);
            }
        }
        return ans;
    }
};

总结

该题的突破口在 y 坐标上,由于 y 坐标的数据范围较小,我们可以暴力遍历 y 坐标,如果当 y 数据范围过大的时候我们需要使用 map<int, vector<int>> mp; 进行存放,因为 map 集合自身有序,所以使用迭代器遍历的时候可以使用二分查找第一个大于等于当前点 y 坐标的 map[y] 迭代器。

这里需要注意的是在代码实现部分使用了 C++ 中自带的二分查找函数 lower_bound(begin, end, num) 表示在数组中查找第一个大于等于 num 的地址,这里需要减去数组的起始地址 begin 得到具体的下标。

  • upper_bound(begin, end, num):第一个大于 num
  • lower_bound(begin, end, num, greater<type>()):查找第一个小于或等于 num
  • upper_bound(begin, end, num, greater<type>()):查找第一个小于 num

结束语

让人生变美好的,不是一生一次的惊喜,而是平常日子里一粥一饭的感动。告别粗糙的生活方式,告别无趣的日常习惯,在每一个看似平淡的日子里找到趣味,找到重塑自我的力量,就有机会开启更加丰富的人生。