You are given a street represented as an infinite number line. There are n
street lamps, each described by lights[i] = [positioni, rangei]
. Each lamp lights up all positions from positioni - rangei
to positioni + rangei
(inclusive).
Your task is to find the brightest position on the street—that is, the position that is covered by the most lamps. If there are multiple such positions, return the smallest one.
Constraints:
1 ≤ n ≤ 105
lights[i][0]
and lights[i][1]
are integers within reasonable bounds (e.g., -109 to 109)At first glance, one might consider simulating the entire number line and counting how many lamps cover each position. However, the number line is infinite, and even if we restrict ourselves to only the positions covered by lamps, the number of possible positions is huge (up to 2 × 109).
Instead, we realize that the number of lamps covering a position only changes at the start or end of a lamp's range. So, rather than checking every position, we can focus on the points where coverage changes.
This leads us to a "sweep line" or "prefix sum" strategy, where we process only the events where lamps start and stop illuminating positions.
We use an event-based sweep line algorithm:
positioni
with rangei
:
positioni - rangei
to increase coverage by 1 (lamp starts illuminating here).positioni + rangei + 1
to decrease coverage by 1 (lamp stops illuminating just after its end).
This approach is efficient because it only processes 2n
events, regardless of the size of the number line.
Example: lights = [[2, 1], [4, 1], [6, 1]]
We create events:
Sorting and sweeping:
class Solution:
def brightestPosition(self, lights):
events = []
for pos, rng in lights:
events.append((pos - rng, 1))
events.append((pos + rng + 1, -1))
events.sort()
max_cover = 0
curr_cover = 0
brightest = None
for pos, delta in events:
curr_cover += delta
if curr_cover > max_cover:
max_cover = curr_cover
brightest = pos
return brightest
class Solution {
public:
int brightestPosition(vector<vector<int>>& lights) {
vector<pair<long long, int>> events;
for (auto& lamp : lights) {
long long pos = lamp[0], rng = lamp[1];
events.emplace_back(pos - rng, 1);
events.emplace_back(pos + rng + 1, -1);
}
sort(events.begin(), events.end());
int maxCover = 0, currCover = 0;
long long brightest = 0;
for (auto& [pos, delta] : events) {
currCover += delta;
if (currCover > maxCover) {
maxCover = currCover;
brightest = pos;
}
}
return static_cast<int>(brightest);
}
};
import java.util.*;
class Solution {
public int brightestPosition(int[][] lights) {
List<long[]> events = new ArrayList<>();
for (int[] lamp : lights) {
long pos = lamp[0], rng = lamp[1];
events.add(new long[]{pos - rng, 1});
events.add(new long[]{pos + rng + 1, -1});
}
events.sort(Comparator.comparingLong(a -> a[0]));
int maxCover = 0, currCover = 0;
long brightest = 0;
for (long[] event : events) {
currCover += event[1];
if (currCover > maxCover) {
maxCover = currCover;
brightest = event[0];
}
}
return (int)brightest;
}
}
var brightestPosition = function(lights) {
let events = [];
for (const [pos, rng] of lights) {
events.push([pos - rng, 1]);
events.push([pos + rng + 1, -1]);
}
events.sort((a, b) => a[0] - b[0]);
let maxCover = 0, currCover = 0, brightest = null;
for (const [pos, delta] of events) {
currCover += delta;
if (currCover > maxCover) {
maxCover = currCover;
brightest = pos;
}
}
return brightest;
};
This makes the solution efficient and scalable even for large inputs.
The key insight is that the number of lamps covering any position only changes at the start or end of a lamp's range. By focusing on these events, we can efficiently find the brightest position using a sweep-line algorithm, processing only O(n) events. This avoids the impracticality of simulating the entire number line and leads to a concise, elegant solution.