Given n horizontal line segments are arranged on the X-axis of a 2D plane. The start and end point of each line segment is given in an nx2 matrix lines[ ][ ], the task is to find the maximum number of intersections possible of any vertical line with the given n line segments.
Examples:
Input: n = 4, lines[][] = [ [ 1, 3 ], [ 2, 3], [ 1, 2 ], [ 4, 4 ] ]
Output: 3
Explanation: A vertical line at X = 2 passes through {1, 3}, {2, 3}, {1, 2}, i.e. three of the given horizontal lines.Input: n= 3, lines[][] = [ [ 1, 3 ], [ 5, 6 ], [ 3, 4 ] ]
Output: 2
Explanation: A vertical line at X = 3 passes through two of the given horizontal lines which are the maximum possible.
This problem is mainly a variation of popular problem called Minimum Platforms.
[Naive Approach] Using Nested Loop â O(n*m) Time and O(1) Space
This approach is based on independently checking the count of every x-axis point that it lies across how many line segments.
- Identify the Range of Vertical Lines: Determine the minimum and maximum x-coordinates from the start and end points of all line segments to define the valid range for vertical lines.
- Check Intersections for Each Vertical Line: For each vertical line within the valid range, check how many line segments intersect with it by comparing the x-coordinate with the start and end points of the segments.
- Track Maximum Intersections: Keep track of the maximum number of line segments that intersect with any vertical line and return this value as the result.
#include <bits/stdc++.h>
using namespace std;
int maxIntersec(vector<vector<int>>& lines) {
int n = lines.size(), maxInt = 0;
int minX = INT_MAX, maxX = INT_MIN;
for (auto& line : lines) {
minX = min(minX, line[0]);
maxX = max(maxX, line[1]);
}
for (int x = minX; x <= maxX; ++x) {
int cnt = 0;
for (auto& line : lines) {
// Check if line intersects vertical line at x
if (line[0] <= x && x <= line[1])
cnt++;
}
maxInt = max(maxInt, cnt);
}
return maxInt;
}
int main() {
vector<vector<int>> lines = {{1, 3}, {5, 6}, {3, 4}};
cout << maxIntersec(lines) << endl;
return 0;
}
import java.util.*;
public class GFG {
public static int maxIntersec(List<int[]> lines) {
int maxInt = 0, minX = Integer.MAX_VALUE, maxX = Integer.MIN_VALUE;
for (int[] line : lines) {
minX = Math.min(minX, line[0]);
maxX = Math.max(maxX, line[1]);
}
for (int x = minX; x <= maxX; ++x) {
int cnt = 0;
for (int[] line : lines) {
// Check if line intersects vertical line at x
if (line[0] <= x && x <= line[1])
cnt++;
}
maxInt = Math.max(maxInt, cnt);
}
return maxInt;
}
public static void main(String[] args) {
List<int[]> lines = Arrays.asList(new int[]{1, 3}, new int[]{2, 3}, new int[]{1, 2}, new int[]{4, 4});
System.out.println(maxIntersec(lines));
}
}
def maxIntersec(lines):
maxInt, minX, maxX = 0, float('inf'), float('-inf')
for line in lines:
minX = min(minX, line[0])
maxX = max(maxX, line[1])
for x in range(minX, maxX + 1):
cnt = 0
for line in lines:
# Check if line intersects vertical line at x
if line[0] <= x <= line[1]:
cnt += 1
maxInt = max(maxInt, cnt)
return maxInt
lines = [[1, 3], [2, 3], [1, 2], [4, 4]]
print(maxIntersec(lines))
using System;
using System.Collections.Generic;
class GFG {
public static int maxIntersec(List<int[]> lines) {
int maxInt = 0, minX = int.MaxValue, maxX = int.MinValue;
foreach (var line in lines) {
minX = Math.Min(minX, line[0]);
maxX = Math.Max(maxX, line[1]);
}
for (int x = minX; x <= maxX; ++x) {
int cnt = 0;
foreach (var line in lines) {
// Check if line intersects vertical line at x
if (line[0] <= x && x <= line[1])
cnt++;
}
maxInt = Math.Max(maxInt, cnt);
}
return maxInt;
}
static void Main() {
var lines = new List<int[]> { new int[] { 1, 3 }, new int[] { 2, 3 }, new int[] { 1, 2 }, new int[] { 4, 4 } };
Console.WriteLine(maxIntersec(lines));
}
}
function maxIntersec(lines) {
let maxInt = 0, minX = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY;
for (let line of lines) {
minX = Math.min(minX, line[0]);
maxX = Math.max(maxX, line[1]);
}
for (let x = minX; x <= maxX; ++x) {
let cnt = 0;
for (let line of lines) {
// Check if line intersects vertical line at x
if (line[0] <= x && x <= line[1])
cnt++;
}
maxInt = Math.max(maxInt, cnt);
}
return maxInt;
}
let lines = [[1, 3], [2, 3], [1, 2], [4, 4]];
console.log(maxIntersec(lines));
Output
2
Time complexity is O(n*m) where n is the size of list and m is the difference between the max and min x points.
[Better Approach] Using Sorting and Two Pointer â O(n log n) Time and O(n) Space
In this approach, we first sort the start and end points of the line segments. We then use two pointers: one for the start points and one for the end points. If the start point is less than or equal to the end point, it means a new line is starting, so we increase the count of intersections. If the start point is greater than the end point, it means a line has ended, so we decrease the intersection count. At each step, we check and update the maximum number of intersections. This method efficiently tracks the intersections without having to check every single point on the x-axis.
- Sort Start and End Points: The start and end points of all the line segments are sorted to efficiently process the intersections.
- Two Pointer Technique: Traverse through the sorted start and end points using two pointers. Increment the intersection count when a new line starts and decrement when a line ends.
- Track Maximum Intersections: Continuously update the maximum number of active intersections during the traversal and return the result.
#include <bits/stdc++.h>
using namespace std;
int maxIntersec(vector<vector<int>>& lines, int n) {
vector<int> start(n), end(n);
for (int i = 0; i < n; ++i) {
start[i] = lines[i][0];
end[i] = lines[i][1];
}
sort(start.begin(), start.end());
sort(end.begin(), end.end());
int i = 0, j = 0, intersect = 0, maxInter = 0;
while (i < n && j < n) {
// If the next line starts before the current one ends
if (start[i] <= end[j]) {
intersect++;
maxInter = max(maxInter, intersect);
i++;
}
// If a line ends before the next line starts,
else {
intersect--;
j++;
}
}
return maxInter;
}
int main() {
int n = 4;
vector<vector<int>> lines = {{1, 3}, {2, 3}, {1, 2}, {4, 4}};
cout << maxIntersec(lines, n) << endl;
return 0;
}
import java.util.*;
public class GFG {
public static int maxIntersec(List<int[]> lines) {
int n = lines.size();
int[] start = new int[n], end = new int[n];
for (int i = 0; i < n; ++i) {
start[i] = lines.get(i)[0];
end[i] = lines.get(i)[1];
}
Arrays.sort(start);
Arrays.sort(end);
int i = 0, j = 0, intersect = 0, maxInter = 0;
while (i < n && j < n) {
if (start[i] <= end[j]) {
intersect++;
maxInter = Math.max(maxInter, intersect);
i++;
} else {
intersect--;
j++;
}
}
return maxInter;
}
public static void main(String[] args) {
List<int[]> lines = Arrays.asList(new int[]{1, 3}, new int[]{2, 3}, new int[]{1, 2}, new int[]{4, 4});
System.out.println(maxIntersec(lines));
}
}
def maxIntersec(lines):
n = len(lines)
start = [lines[i][0] for i in range(n)]
end = [lines[i][1] for i in range(n)]
start.sort()
end.sort()
i, j, intersect, maxInter = 0, 0, 0, 0
while i < n and j < n:
if start[i] <= end[j]:
intersect += 1
maxInter = max(maxInter, intersect)
i += 1
else:
intersect -= 1
j += 1
return maxInter
lines = [[1, 3], [2, 3], [1, 2], [4, 4]]
print(maxIntersec(lines))
using System;
using System.Collections.Generic;
class Program {
public static int maxIntersec(List<int[]> lines) {
int n = lines.Count;
int[] start = new int[n], end = new int[n];
for (int k = 0; k < n; ++k) {
start[k] = lines[k][0];
end[k] = lines[k][1];
}
Array.Sort(start);
Array.Sort(end);
int i = 0, j = 0, intersect = 0, maxInter = 0;
while (i < n && j < n) {
if (start[i] <= end[j]) {
intersect++;
maxInter = Math.Max(maxInter, intersect);
i++;
} else {
intersect--;
j++;
}
}
return maxInter;
}
static void Main() {
var lines = new List<int[]> { new int[] { 1, 3 }, new int[] { 2, 3 }, new int[] { 1, 2 }, new int[] { 4, 4 } };
Console.WriteLine(maxIntersec(lines));
}
}
function maxIntersec(lines) {
const n = lines.length;
const start = lines.map(line => line[0]);
const end = lines.map(line => line[1]);
start.sort((a, b) => a - b);
end.sort((a, b) => a - b);
let i = 0, j = 0, intersect = 0, maxInter = 0;
while (i < n && j < n) {
if (start[i] <= end[j]) {
intersect++;
maxInter = Math.max(maxInter, intersect);
i++;
} else {
intersect--;
j++;
}
}
return maxInter;
}
const lines = [[1, 3], [2, 3], [1, 2], [4, 4]];
console.log(maxIntersec(lines));
Output
3
[Expected Approach] Using Hash Map â O(n) Time and O(n) Space
Instead of checking all x-axis points, we focus only on the start and end points of the line segments. We use a map to track these points: incrementing the count when a line starts and decrementing it when a line ends. After processing all points, we simply check the map to find the point with the highest count, which gives the maximum number of intersections. This method is efficient as we only consider the relevant points.
- Create Events: Treat the start of a line as an increment and the end (adjusted by +1) as a decrement to track active line segments.
- Use of Map: Store events in a sorted map where keys are x-coordinates and values are the count of active lines.
- Process Events: Traverse the events, updating the active count at each x-coordinate and track the maximum intersections.
#include <bits/stdc++.h>
using namespace std;
int maxIntersec(vector<vector<int>>& lines) {
unordered_map<int, int> events;
// Create events for each line segment
for (auto& line : lines) {
events[line[0]]++;
events[line[1] + 1]--;
}
int cnt = 0, maxCnt = 0;
for (auto& event : events) {
cnt += event.second;
maxCnt = max(maxCnt, cnt);
}
return maxCnt;
}
int main() {
vector<vector<int>> lines = {{1, 3}, {5, 6}, {3, 4}};
cout << maxIntersec(lines) << endl;
return 0;
}
import java.util.*;
public class GFG {
public static int maxIntersec(List<int[]> lines) {
Map<Integer, Integer> events = new HashMap<>();
// Create events for each line segment
for (int[] line : lines) {
events.put(line[0], events.getOrDefault(line[0], 0) + 1);
events.put(line[1] + 1, events.getOrDefault(line[1] + 1, 0) - 1);
}
int cnt = 0, maxCnt = 0;
for (int value : events.values()) {
cnt += value;
maxCnt = Math.max(maxCnt, cnt);
}
return maxCnt;
}
public static void main(String[] args) {
List<int[]> lines = Arrays.asList(new int[]{1, 3}, new int[]{5, 6}, new int[]{3, 4});
System.out.println(maxIntersec(lines));
}
}
def maxIntersec(lines):
events = {}
# Create events for each line segment
for line in lines:
events[line[0]] = events.get(line[0], 0) + 1
events[line[1] + 1] = events.get(line[1] + 1, 0) - 1
cnt, maxCnt = 0, 0
for value in events.values():
cnt += value
maxCnt = max(maxCnt, cnt)
return maxCnt
lines = [[1, 3], [5, 6], [3, 4]]
print(maxIntersec(lines))
using System;
using System.Collections.Generic;
class Program {
public static int maxIntersec(List<int[]> lines) {
Dictionary<int, int> events = new Dictionary<int, int>();
// Create events for each line segment
foreach (var line in lines) {
if (!events.ContainsKey(line[0]))
events[line[0]] = 0;
events[line[0]]++;
if (!events.ContainsKey(line[1] + 1))
events[line[1] + 1] = 0;
events[line[1] + 1]--;
}
int cnt = 0, maxCnt = 0;
foreach (var eventCount in events.Values) {
cnt += eventCount;
maxCnt = Math.Max(maxCnt, cnt);
}
return maxCnt;
}
static void Main() {
var lines = new List<int[]> { new int[] { 1, 3 }, new int[] { 5, 6 }, new int[] { 3, 4 } };
Console.WriteLine(maxIntersec(lines));
}
}
function maxIntersec(lines) {
let events = {};
// Create events for each line segment
for (let line of lines) {
events[line[0]] = (events[line[0]] || 0) + 1;
events[line[1] + 1] = (events[line[1] + 1] || 0) - 1;
}
let cnt = 0, maxCnt = 0;
for (let value of Object.values(events)) {
cnt += value;
maxCnt = Math.max(maxCnt, cnt);
}
return maxCnt;
}
const lines = [[1, 3], [5, 6], [3, 4]];
console.log(maxIntersec(lines));
Output
2