通过万岁!!!
- 题目:就是给你一个点,然后给你一个角度(也就是视野的角度angle)。可以在这个点上转动,但不能移动。看看周围的点,最多多少个出现在你的视野中。点都是在平面直角坐标系上的,给出一个二维数组,数组中就是点的横坐标和纵坐标。
- 思路:也就是说,我们的角度是angle,角有两条边我们转动起始边即可。可以想到一种类似滑动窗口,我们的窗口大小是固定的,然后看里面最多能出现多少个点。那我们以角度为单位,将目标点的角度计算出现。
- 注意:这里题目中有几个比较坑。就是自己所占的点,也可能是目标点。
- 技巧:
- 首先是将角度计算出来,然后存到一个数组中,这里需要对这个数组进行排序的。
- 其次,滑动窗口其实就是两个指针,我们left从0开始,然后right进行++,++的前提是指向的角度要小于我们的视野角,否则让left++。
- 然后就是java中的,java的arctan公式,也就是atan2()计算出来的结果不是0到360,而是0到2PI。而给出的angle是角度0到360的形式。
- 并且,因为我们的角度是0到2PI,所以我们还需要考虑当left转动一圈后快要到达2PI的时候,因为到达2PI就是一圈了,我们就可以停止了。这时候我们可以对数组进行补全。也就是说,我们计算出来的角度小于angle的时候,需要将这些点+2PI。当然,可以直接将数组复制一分,添加到后面,但是空间复杂度会略高。
伪代码
定义一个自己所在的点loc
定义目标点角度的数组angles
遍历目标点,计算点的角度
如果是自己所在点,loc++,并且continue。
计算角度值目标点与所占点之间的角度。并添加到angles中。
angles数组进行排序
遍历angles数组,将小于angle的元素+2PI后添加到数组中。(这里只能用fori的循环遍历,并且条件是小于angles.size(),而这个值必须要提前存储,否则添加元素数组就变了,这样会报并发错误)
定义max,用来存储最大的点数。
双指针遍历数组,条件是right要小于angles的长度
如果,right指向的角度-left指向的角度小于视野角,那么right++
否则,取right-left和max的最大值。并且left++
return loc+max;
java代码
class Solution {
public int visiblePoints(List<List<Integer>> points, int angle, List<Integer> location) {
int loc = 0;// 自己所在点,也是可以检测到的
ArrayList<Double> angles = new ArrayList(points.size());
for (List<Integer> point : points) {
if (point.get(0) == location.get(0) && point.get(1) == location.get(1)) {
loc++;
continue;
}
angles.add(Math.atan2(point.get(0) - location.get(0), point.get(1) - location.get(1)));// 角度,是PI的那种形式
}
Collections.sort(angles);
// 使用双指针遍历angles数组,两个指针之间的距离不能超过angle,让慢指针需要到达360,因此需要将angles中角度小于angle的加上360
double anglePI = angle * Math.PI / 180;// 将给定的角度转换为PI的形式
int temp = angles.size();// 这里必须这样使用fori,不然会数组一直遍历不完
for (int i = 0; i < temp; i++) {
if (angles.get(i) < anglePI) angles.add(angles.get(i) + 2 * Math.PI);
else break;
}
temp = 0;// 这个就是max
int left = 0, right = 0;
while (right < angles.size()) {
if (angles.get(right) - angles.get(left) <= anglePI) {
right++;
} else {
temp = temp > (right - left) ? temp : right - left;
left++;
}
}
return temp + loc;
}
}
- 总结:这个题技巧性比较强,我们必须要将二维的点,变成一维的角,然后使用滑动窗口。其实滑动窗口就是双指针。