Maximum Number of Visible Points
Compute angles relative to viewer, sort, sliding-window an arc of size `angle` — handle wraparound by doubling the array.
Problem#
Standing at location, you can rotate but not move. You see any point within angle degrees of your facing direction. Some points may be at the same spot as you (always visible). Return the maximum count of visible points.
Examples#
points=[[2,1],[2,2],[3,3]], angle=90, location=[1,1]→3.points=[[1,0],[2,1]], angle=13, location=[1,1]→1.
Constraints#
1 <= points.length <= 10^5,0 <= angle < 360.
Approach#
Count co-located points separately. For the rest, compute angle from viewer in degrees via atan2. Sort ascending; duplicate by adding 360 to each angle to handle wrap. Sliding window finds the longest run within angle.
Solution#
class Solution {public: int visiblePoints(vector<vector<int>>& points, int angle, vector<int>& location) { int same = 0; vector<double> angs; for (auto& p : points) { int dx = p[0] - location[0], dy = p[1] - location[1]; if (dx == 0 && dy == 0) { ++same; continue; } angs.push_back(atan2((double)dy, (double)dx) * 180.0 / M_PI); } sort(angs.begin(), angs.end()); int n = angs.size(); for (int i = 0; i < n; ++i) angs.push_back(angs[i] + 360.0); int l = 0, best = 0; for (int r = 0; r < (int)angs.size(); ++r) { while (angs[r] - angs[l] > (double)angle + 1e-9) ++l; best = max(best, r - l + 1); } return best + same; }};import ( "math" "sort")
func visiblePoints(points [][]int, angle int, location []int) int { same := 0 angs := []float64{} for _, p := range points { dx := p[0] - location[0] dy := p[1] - location[1] if dx == 0 && dy == 0 { same++ continue } angs = append(angs, math.Atan2(float64(dy), float64(dx))*180.0/math.Pi) } sort.Float64s(angs) n := len(angs) for i := 0; i < n; i++ { angs = append(angs, angs[i]+360.0) } l, best := 0, 0 for r := 0; r < len(angs); r++ { for angs[r]-angs[l] > float64(angle)+1e-9 { l++ } if r-l+1 > best { best = r - l + 1 } } return best + same}import mathfrom typing import List
class Solution: def visiblePoints(self, points: List[List[int]], angle: int, location: List[int]) -> int: same = 0 angs: List[float] = [] for x, y in points: dx, dy = x - location[0], y - location[1] if dx == 0 and dy == 0: same += 1 continue angs.append(math.atan2(dy, dx) * 180.0 / math.pi) angs.sort() n = len(angs) angs.extend(a + 360.0 for a in angs[:n]) l = 0 best = 0 for r in range(len(angs)): while angs[r] - angs[l] > angle + 1e-9: l += 1 best = max(best, r - l + 1) return best + samefunction visiblePoints(points, angle, location) { let same = 0; const angs = []; for (const [x, y] of points) { const dx = x - location[0]; const dy = y - location[1]; if (dx === 0 && dy === 0) { same++; continue; } angs.push(Math.atan2(dy, dx) * 180.0 / Math.PI); } angs.sort((a, b) => a - b); const n = angs.length; for (let i = 0; i < n; i++) angs.push(angs[i] + 360.0); let l = 0; let best = 0; for (let r = 0; r < angs.length; r++) { while (angs[r] - angs[l] > angle + 1e-9) l++; best = Math.max(best, r - l + 1); } return best + same;}class Solution { public int visiblePoints(List<List<Integer>> points, int angle, List<Integer> location) { int same = 0; List<Double> angs = new ArrayList<>(); int lx = location.get(0), ly = location.get(1); for (List<Integer> p : points) { int dx = p.get(0) - lx, dy = p.get(1) - ly; if (dx == 0 && dy == 0) { same++; continue; } angs.add(Math.atan2(dy, dx) * 180.0 / Math.PI); } Collections.sort(angs); int n = angs.size(); for (int i = 0; i < n; i++) angs.add(angs.get(i) + 360.0); int l = 0, best = 0; for (int r = 0; r < angs.size(); r++) { while (angs.get(r) - angs.get(l) > angle + 1e-9) l++; best = Math.max(best, r - l + 1); } return best + same; }}function visiblePoints(points: number[][], angle: number, location: number[]): number { let same = 0; const angs: number[] = []; for (const [x, y] of points) { const dx = x - location[0]; const dy = y - location[1]; if (dx === 0 && dy === 0) { same++; continue; } angs.push(Math.atan2(dy, dx) * 180.0 / Math.PI); } angs.sort((a, b) => a - b); const n = angs.length; for (let i = 0; i < n; i++) angs.push(angs[i] + 360.0); let l = 0; let best = 0; for (let r = 0; r < angs.length; r++) { while (angs[r] - angs[l] > angle + 1e-9) l++; best = Math.max(best, r - l + 1); } return best + same;}Editorial#
Sorting angles plus the “doubled array” trick lets a sliding window naturally cross the 0/360 boundary. Co-located points must be handled out-of-band since their angle is undefined. The tiny epsilon 1e-9 makes the float comparison forgiving at the angle boundary.
Complexity#
- Time:
O(n log n)for the sort. - Space:
O(n).
Concept revision#
Related#