🧠
Optimal Approach with Early Termination and Integer Slope Representation
💡 This approach adds early termination to reduce unnecessary computations and uses integer slope representation with gcd normalization, making it optimal for large inputs.
Intuition
By sorting points or breaking early when the remaining points cannot beat the current max, we reduce runtime. Using integer slopes avoids floating-point errors and ensures correctness.
Algorithm
- Initialize max_points to 1.
- For each point i, if max_points >= n - i or max_points > n/2, break early as no better line is possible.
- For each other point j > i, compute reduced slope as integer pair using gcd.
- Count duplicates and slopes in a hashmap.
- Update max_points with the maximum count found plus duplicates plus 1.
💡 Early termination reduces runtime by skipping points that cannot improve the max. Integer slope representation ensures correctness.
from collections import defaultdict
import math
def maxPoints(points):
n = len(points)
if n <= 2:
return n
max_points = 1
points.sort() # Sort to enable early termination
for i in range(n):
if max_points >= n - i or max_points > n // 2:
break
slopes = defaultdict(int)
duplicates = 0
for j in range(i + 1, n):
dx = points[j][0] - points[i][0]
dy = points[j][1] - points[i][1]
if dx == 0 and dy == 0:
duplicates += 1
continue
gcd = math.gcd(dy, dx)
if gcd != 0:
dy //= gcd
dx //= gcd
if dx < 0:
dx = -dx
dy = -dy
slopes[(dy, dx)] += 1
current_max = max(slopes.values(), default=0) + duplicates + 1
max_points = max(max_points, current_max)
return max_points
# Driver code
points = [[1,1],[2,2],[3,3]]
print(maxPoints(points)) # Output: 3
Line Notes
points.sort()Sort points to enable early termination optimization
if max_points >= n - i or max_points > n // 2:Early break if no better line can be found
gcd = math.gcd(dy, dx)Reduce slope fraction to normalized form
slopes[(dy, dx)] += 1Count points sharing the same slope with base point
import java.util.*;
public class MaxPointsOnLine {
private static int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}
public static int maxPoints(int[][] points) {
int n = points.length;
if (n <= 2) return n;
Arrays.sort(points, Comparator.comparingInt(a -> a[0]));
int maxPoints = 1;
for (int i = 0; i < n; i++) {
if (maxPoints >= n - i || maxPoints > n / 2) break;
Map<String, Integer> slopes = new HashMap<>();
int duplicates = 0;
for (int j = i + 1; j < n; j++) {
int dx = points[j][0] - points[i][0];
int dy = points[j][1] - points[i][1];
if (dx == 0 && dy == 0) {
duplicates++;
continue;
}
int gcd = gcd(dy, dx);
if (gcd != 0) {
dy /= gcd;
dx /= gcd;
}
if (dx < 0) {
dx = -dx;
dy = -dy;
}
String slope = dy + "," + dx;
slopes.put(slope, slopes.getOrDefault(slope, 0) + 1);
}
int currentMax = slopes.values().stream().max(Integer::compare).orElse(0) + duplicates + 1;
maxPoints = Math.max(maxPoints, currentMax);
}
return maxPoints;
}
public static void main(String[] args) {
int[][] points = {{1,1},{2,2},{3,3}};
System.out.println(maxPoints(points)); // Output: 3
}
}
Line Notes
Arrays.sort(points, Comparator.comparingInt(a -> a[0]));Sort points by x-coordinate for early termination
if (maxPoints >= n - i || maxPoints > n / 2) break;Stop early if no better line can be found
int gcd = gcd(dy, dx);Reduce slope fraction to normalized form
slopes.put(slope, slopes.getOrDefault(slope, 0) + 1);Count points sharing the same slope
#include <iostream>
#include <vector>
#include <unordered_map>
#include <string>
#include <algorithm>
using namespace std;
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
int maxPoints(vector<vector<int>>& points) {
int n = points.size();
if (n <= 2) return n;
sort(points.begin(), points.end());
int max_points = 1;
for (int i = 0; i < n; i++) {
if (max_points >= n - i || max_points > n / 2) break;
unordered_map<string, int> slopes;
int duplicates = 0;
for (int j = i + 1; j < n; j++) {
int dx = points[j][0] - points[i][0];
int dy = points[j][1] - points[i][1];
if (dx == 0 && dy == 0) {
duplicates++;
continue;
}
int g = gcd(dy, dx);
if (g != 0) {
dy /= g;
dx /= g;
}
if (dx < 0) {
dx = -dx;
dy = -dy;
}
string slope = to_string(dy) + "," + to_string(dx);
slopes[slope]++;
}
int current_max = 0;
for (auto& p : slopes) current_max = max(current_max, p.second);
max_points = max(max_points, current_max + duplicates + 1);
}
return max_points;
}
int main() {
vector<vector<int>> points = {{1,1},{2,2},{3,3}};
cout << maxPoints(points) << endl; // Output: 3
return 0;
}
Line Notes
sort(points.begin(), points.end());Sort points to enable early termination optimization
if (max_points >= n - i || max_points > n / 2) break;Early break to reduce unnecessary computations
int g = gcd(dy, dx);Reduce slope fraction to normalized form
slopes[slope]++;Count points sharing the same slope
function gcd(a, b) {
return b === 0 ? a : gcd(b, a % b);
}
function maxPoints(points) {
const n = points.length;
if (n <= 2) return n;
points.sort((a, b) => a[0] - b[0]);
let maxPoints = 1;
for (let i = 0; i < n; i++) {
if (maxPoints >= n - i || maxPoints > n / 2) break;
const slopes = new Map();
let duplicates = 0;
for (let j = i + 1; j < n; j++) {
let dx = points[j][0] - points[i][0];
let dy = points[j][1] - points[i][1];
if (dx === 0 && dy === 0) {
duplicates++;
continue;
}
let g = gcd(dy, dx);
if (g !== 0) {
dy /= g;
dx /= g;
}
if (dx < 0) {
dx = -dx;
dy = -dy;
}
const slope = dy + ',' + dx;
slopes.set(slope, (slopes.get(slope) || 0) + 1);
}
let currentMax = 0;
for (const count of slopes.values()) {
if (count > currentMax) currentMax = count;
}
maxPoints = Math.max(maxPoints, currentMax + duplicates + 1);
}
return maxPoints;
}
// Test
console.log(maxPoints([[1,1],[2,2],[3,3]])); // Output: 3
Line Notes
points.sort((a, b) => a[0] - b[0]);Sort points by x-coordinate for early termination
if (maxPoints >= n - i || maxPoints > n / 2) break;Stop early if no better line can be found
let g = gcd(dy, dx);Reduce slope fraction to normalized form
slopes.set(slope, (slopes.get(slope) || 0) + 1);Count points sharing the same slope
TimeO(n^2) with early termination
SpaceO(n)
Early termination reduces average runtime by skipping unnecessary checks. Still worst-case O(n^2) but faster in practice.
💡 For large n, early breaks can save significant time, making this approach practical for interviews.
Interview Verdict: Accepted / Optimal for interviews
This is the best practical approach combining correctness, efficiency, and code clarity.