🧠
Line Sweep Algorithm with Events
💡 This approach uses a line sweep technique to process interval start and end points and query points in sorted order, counting active intervals efficiently.
Intuition
Create events for interval starts (+1), interval ends (-1), and points (query). Sweep through all events sorted by coordinate, updating active interval count and recording counts for points.
Algorithm
- Create a list of events: (coordinate, type, index), where type is +1 for interval start, -1 for interval end + 1, and 0 for points.
- Sort all events by coordinate; if tie, process starts before points before ends.
- Initialize active interval count to zero.
- Sweep through events: increment count on start, record count for points, decrement count on end.
- Return recorded counts for points in original order.
💡 The event sorting and processing order ensures correct active interval counts at each point.
def count_intervals_line_sweep(intervals, points):
events = []
for start, end in intervals:
events.append((start, 1, -1)) # interval start
events.append((end + 1, -1, -1)) # interval end + 1
for i, p in enumerate(points):
events.append((p, 0, i)) # point query
events.sort(key=lambda x: (x[0], -x[1])) # start(+1) before point(0) before end(-1)
active = 0
result = [0] * len(points)
for coord, typ, idx in events:
if typ == 1:
active += 1
elif typ == -1:
active -= 1
else:
result[idx] = active
return result
# Driver code
if __name__ == '__main__':
intervals = [[1,4],[2,5],[7,9]]
points = [2,5,8]
print(count_intervals_line_sweep(intervals, points)) # Output: [2,1,1]
Line Notes
events.append((start, 1, -1))Mark interval start event with +1 type
events.append((end + 1, -1, -1))Mark interval end event at end+1 with -1 type to exclude end point
events.append((p, 0, i))Mark point query event with 0 type and index
events.sort(key=lambda x: (x[0], -x[1]))Sort events by coordinate; starts before points before ends
if typ == 1: active += 1Increment active intervals on start
elif typ == -1: active -= 1Decrement active intervals on end
else: result[idx] = activeRecord active intervals count for point
import java.util.*;
public class CountIntervalsLineSweep {
static class Event implements Comparable<Event> {
int coord, type, index;
Event(int c, int t, int i) { coord = c; type = t; index = i; }
public int compareTo(Event other) {
if (this.coord != other.coord) return Integer.compare(this.coord, other.coord);
return Integer.compare(other.type, this.type); // start(1) before point(0) before end(-1)
}
}
public static int[] countIntervalsLineSweep(int[][] intervals, int[] points) {
List<Event> events = new ArrayList<>();
for (int[] interval : intervals) {
events.add(new Event(interval[0], 1, -1));
events.add(new Event(interval[1] + 1, -1, -1));
}
for (int i = 0; i < points.length; i++) {
events.add(new Event(points[i], 0, i));
}
Collections.sort(events);
int active = 0;
int[] result = new int[points.length];
for (Event e : events) {
if (e.type == 1) active++;
else if (e.type == -1) active--;
else result[e.index] = active;
}
return result;
}
public static void main(String[] args) {
int[][] intervals = {{1,4},{2,5},{7,9}};
int[] points = {2,5,8};
System.out.println(Arrays.toString(countIntervalsLineSweep(intervals, points))); // [2,1,1]
}
}
Line Notes
events.add(new Event(interval[0], 1, -1));Add interval start event with type +1
events.add(new Event(interval[1] + 1, -1, -1));Add interval end event at end+1 with type -1
events.add(new Event(points[i], 0, i));Add point query event with type 0 and index
Collections.sort(events);Sort events by coordinate and type order
if (e.type == 1) active++;Increment active intervals on start event
else if (e.type == -1) active--;Decrement active intervals on end event
else result[e.index] = active;Record active intervals count for point
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Event {
int coord, type, index;
Event(int c, int t, int i) : coord(c), type(t), index(i) {}
bool operator<(const Event& other) const {
if (coord != other.coord) return coord < other.coord;
return type > other.type; // start(1) before point(0) before end(-1)
}
};
vector<int> countIntervalsLineSweep(const vector<pair<int,int>>& intervals, const vector<int>& points) {
vector<Event> events;
for (auto& interval : intervals) {
events.emplace_back(interval.first, 1, -1);
events.emplace_back(interval.second + 1, -1, -1);
}
for (int i = 0; i < (int)points.size(); i++) {
events.emplace_back(points[i], 0, i);
}
sort(events.begin(), events.end());
int active = 0;
vector<int> result(points.size());
for (auto& e : events) {
if (e.type == 1) active++;
else if (e.type == -1) active--;
else result[e.index] = active;
}
return result;
}
int main() {
vector<pair<int,int>> intervals = {{1,4},{2,5},{7,9}};
vector<int> points = {2,5,8};
vector<int> res = countIntervalsLineSweep(intervals, points);
for (int c : res) cout << c << ' ';
cout << '\n'; // Output: 2 1 1
return 0;
}
Line Notes
events.emplace_back(interval.first, 1, -1);Add interval start event with type +1
events.emplace_back(interval.second + 1, -1, -1);Add interval end event at end+1 with type -1
events.emplace_back(points[i], 0, i);Add point query event with type 0 and index
sort(events.begin(), events.end());Sort events by coordinate and type order
if (e.type == 1) active++;Increment active intervals on start event
else if (e.type == -1) active--;Decrement active intervals on end event
else result[e.index] = active;Record active intervals count for point
function countIntervalsLineSweep(intervals, points) {
const events = [];
for (const [start, end] of intervals) {
events.push([start, 1, -1]); // interval start
events.push([end + 1, -1, -1]); // interval end + 1
}
points.forEach((p, i) => events.push([p, 0, i]));
events.sort((a, b) => a[0] - b[0] || b[1] - a[1]); // start(1) before point(0) before end(-1)
let active = 0;
const result = new Array(points.length).fill(0);
for (const [coord, type, idx] of events) {
if (type === 1) active++;
else if (type === -1) active--;
else result[idx] = active;
}
return result;
}
// Test
const intervals = [[1,4],[2,5],[7,9]];
const points = [2,5,8];
console.log(countIntervalsLineSweep(intervals, points)); // [2,1,1]
Line Notes
events.push([start, 1, -1]);Add interval start event with type +1
events.push([end + 1, -1, -1]);Add interval end event at end+1 with type -1
points.forEach((p, i) => events.push([p, 0, i]));Add point query events with type 0 and index
events.sort((a, b) => a[0] - b[0] || b[1] - a[1]);Sort events by coordinate and type order
if (type === 1) active++;Increment active intervals on start event
else if (type === -1) active--;Decrement active intervals on end event
else result[idx] = active;Record active intervals count for point
TimeO((n + m) log (n + m))
SpaceO(n + m)
We create 2n + m events and sort them, which takes O((n + m) log (n + m)). Sweeping is O(n + m).
💡 For 100,000 intervals and points, sorting about 300,000 events is efficient and practical.
Interview Verdict: Accepted
This is the optimal approach for large inputs and is a common pattern in interval problems.