题目

类型:几何

image.png
image.png

解题思路

我们只需要算出每个坐标相对于 location 与 x 轴的夹角,然后,找到以每个坐标为起点,放置 angle 角度,这么大的辐射范围内的点数的最大值即可
image.png

假设,我们有上图所示的坐标系,里面有一些点,人所在的位置如图中小人标识位置,假设给定的辐射范围 angle 为 90°,那么,我们的计算过程如下:

  • 先算出每个点与人位置坐标与 x 轴的夹角;
  • 把这些点扔到 list 里面,并排序;
  • 为了处理 180° 到 -180° 的过度,我们可以把所有的坐标加上 360° 再加一遍到 list 中。
  • 遍历每一个坐标夹角 x,统计[x, x+angle]范围内的点数,这个过程我们可以使用滑动窗口或者二分查找实现,最后返回最大的点数即可。
  • 注意,题目约定了你所在的位置也可能存在点,这些点需要特殊处理。

另外,本题我们可以使用库函数 atan2直接计算出夹角对应的弧度值,atan2 的返回值为[-π, π]
image.png

代码

  1. class Solution {
  2. public int visiblePoints(List<List<Integer>> points, int angle, List<Integer> location) {
  3. // 算出每一个坐标相对于location位置与x轴的夹角(弧度制),扔到List中排序
  4. // 然后对于每一个点,使用二分或滑动窗口找出小于这个点+angle(转成弧度制)的最大坐标点
  5. // 两者之间的下标差就是从这个点出发,辐射angle角度的点数量
  6. // 注意,与location点相同的点的特殊处理
  7. int same = 0;
  8. List<Double> list = new ArrayList<>();
  9. int x = location.get(0), y = location.get(1);
  10. for (List<Integer> point : points) {
  11. int a = point.get(0), b = point.get(1);
  12. if (a == x && b == y) {
  13. // 与location同点
  14. same++;
  15. } else {
  16. // 计算角度(弧度制)
  17. list.add(Math.atan2(b - y, a - x));
  18. }
  19. }
  20. // 排序
  21. Collections.sort(list);
  22. // 把前面所有的数添加一遍到后面,类似于于循环数组的使用
  23. int size = list.size();
  24. for (int i = 0; i < size; i++) {
  25. // 加 360度,然后范围相当于变成了 [-pi, 3*pi]
  26. list.add(list.get(i) + 2 * Math.PI);
  27. }
  28. double angleDegree = angle * Math.PI / 180;
  29. int max = 0;
  30. int i = 0, j = 0;
  31. while (i < size) {
  32. // 滑动窗口,简单点,list是有序的,使用二分查找也是可以的
  33. while (j < 2 * size && list.get(j) - list.get(i) <= angleDegree) {
  34. j++;
  35. }
  36. max = Math.max(max, j - i);
  37. i++;
  38. }
  39. return max + same;
  40. }
  41. }