一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第30天,点击查看活动详情。
题目链接:6043. 统计包含每个点的矩形数目
题目描述
给你一个二维整数数组 rectangles ,其中 rectangles[i] = [li, hi] 表示第 i 个矩形长为 高为 。给你一个二维整数数组 points ,其中 points[j] = [xj, yj] 是坐标为 (xj, yj) 的一个点。
第 i 个矩形的 左下角 在 (0, 0) 处,右上角 在 。
请你返回一个整数数组 count ,长度为 points.length,其中 count[j] 是 包含 第 j 个点的矩形数目。
如果 且 ,那么我们说第 i 个矩形包含第 j 个点。如果一个点刚好在矩形的 边上 ,这个点也被视为被矩形包含。
提示:
- 所有
rectangles互不相同 。 - 所有
points互不相同 。
示例 1:
输入: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:
输入: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坐标最大1e9,y坐标最大100很明显y坐标的数据范围较小可以作为我们解题的突破口。
由于 y 坐标最大为 100,我们对 rectangles 矩形右上角的点数据按照 y 坐标进行分类,查询某个点 points[i] 被多少个矩形覆盖时,我们只需要遍历大于等于该点 y 坐标的所有矩形(最多 100 个),统计同时大于等于该点 x 坐标的所有矩形即可。
这里可以提前将所有 y 坐标相同的矩形对 x 坐标进行排序,这样我们在统计大于等于 x 坐标的矩形时可以使用二分查找进行快速判断。
具体实现
- 对矩形右上角的点数据
rectangles进行分类处理,将相同y坐标的点放入一组数据中,这里可以使用map<int, vector<int>> mp;进行存放(mp[y] = {x, ...}),也可以直接开一个二维矢量数组vector<int> y[101];进行存放(y[y] = {x, ...}),因为y最大为100所以需要开101个空间。 - 遍历
points,每次遍历大于等于该点y坐标的所有矩形,同时二分查找并统计大于等于该点x坐标的矩形个数。
复杂度分析
- 时间复杂度:,
n为矩形的个数,m为点的个数。 - 空间复杂度:,仅需存放
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):第一个大于numlower_bound(begin, end, num, greater<type>()):查找第一个小于或等于numupper_bound(begin, end, num, greater<type>()):查找第一个小于num
结束语
让人生变美好的,不是一生一次的惊喜,而是平常日子里一粥一饭的感动。告别粗糙的生活方式,告别无趣的日常习惯,在每一个看似平淡的日子里找到趣味,找到重塑自我的力量,就有机会开启更加丰富的人生。