Given n points on a 2D plane, find the maximum number of points that lie on the same straight line.
OR
Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane, return the maximum number of points that lie on the same straight line.
The equation of a line is expressed as y = mx + c, where y and x are variables, and m and c represent the slope and y-intercept respectively.
To solve this problem, one can count the number of points sharing the same slope relative to each starting point. Special cases such as duplicate points and vertical lines require careful handling. For vertical lines, the slope is undefined, but they can be assigned a distinct representation like infinity.
The slope can be calculated as m = (y2 - y1) / (x2 - x1), which results in a double value. However, using doubles as keys in a HashMap can be problematic due to precision concerns.
While it is generally not ideal to use doubles as keys, it is acceptable in this problem if edge cases involving gradient 0.0 are managed correctly. Double values treat +0.0 and -0.0 as distinct keys, which avoids conflicts. Here is a key example:
1
double m =(p2.y== p1.y)?0.0:((double)(p2.y- p1.y)/(p2.x- p1.x));
Despite this workaround, large numbers risk hitting limitations in double precision, which can lead to overflow and inaccuracies.
publicclassSolution {
publicintmaxPoints(int[][] points) {
int n = points.length;
if (n < 2) return n;
int ans = 0;
for (int i = 0; i < n; i++) {
HashMap<Double, Integer> slopeCount =new HashMap<>();
int duplicate = 0;
int max = 0;
for (int j = 0; j < n; j++) {
if (i == j) continue;
int dx = points[j][0]- points[i][0];
int dy = points[j][1]- points[i][1];
if (dx == 0 && dy == 0) {
duplicate++;
continue;
}
// Calculate the slope as a double (dy/dx)double slope = dx == 0 ? Double.POSITIVE_INFINITY : (double) dy / dx;
slopeCount.put(slope, slopeCount.getOrDefault(slope, 0) + 1);
max = Math.max(max, slopeCount.get(slope));
}
ans = Math.max(ans, max + duplicate + 1); // Include duplicates and the current point }
return ans;
}
}
classSolution:
defmaxPoints(self, points: List[List[int]]) -> int:
n = len(points)
if n <2:
return n
ans =0for i in range(n):
slope_count: defaultdict[float, int] = defaultdict(int)
duplicate =0 max_pts =0for j in range(n):
if i == j:
continue dx = points[j][0] - points[i][0]
dy = points[j][1] - points[i][1]
if dx ==0and dy ==0:
duplicate +=1continue# Calculate the slope as dy / dx using float precision slope = float('inf') if dx ==0else dy / dx
slope_count[slope] +=1 max_pts = max(max_pts, slope_count[slope])
ans = max(ans, max_pts + duplicate +1) # Including duplicates and the current pointreturn ans
The problem is to determine the maximum number of points that lie on the same straight line. To achieve this:
Basic Geometry: Any two points define a straight line. If we compute slopes of lines formed by a pair of points, we can group points with the same slope to determine the maximum collinear set of points.
Handling Precision: Since slopes can be represented as a fraction (dy/dx), we should ensure precision by using the greatest common divisor (GCD) to simplify the fraction.
Algorithm:
Iterate through each point in the array.
Calculate the slope of lines formed with every other point in the array.
Store slopes in a hashmap to count occurrences of each slope.
For each iteration of the outer loop, track the maximum count of collinear points (including the point itself).
This approach is slower compared to the standard solution that uses doubles for storing slopes. However, it ensures complete precision when working with integer coordinates.
A pairing function can uniquely represent a pair of integers, as explained here: https://en.wikipedia.org/wiki/Pairing_function#Cantor_pairing_function. In this method, the pairing function is used to encode a fraction by treating the numerator and denominator as a unique pair of integers, thus eliminating potential precision issues associated with using floats or doubles.
publicclassSolution {
publicintmaxPoints(int[][] points) {
int ans = 0;
int n = points.length;
for (int i = 0; i < n; i++) {
HashMap<String, Integer> slopeCount =new HashMap<>();
int duplicate = 0, max = 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) {
duplicate++;
continue;
}
// Simplify slope using GCDint gcd = gcd(dx, dy);
dx /= gcd;
dy /= gcd;
String slope = dy +"/"+ dx; // Represent slope as string to handle precision slopeCount.put(slope, slopeCount.getOrDefault(slope, 0) + 1);
max = Math.max(max, slopeCount.get(slope));
}
ans = Math.max(ans, max + duplicate + 1); // Include duplicates and the current point }
return ans;
}
privateintgcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
}
classSolution:
defmaxPoints(self, points: List[List[int]]) -> int:
n = len(points)
ans =0for i in range(n):
slope_count: defaultdict[str, int] = defaultdict(int)
duplicate =0 max_pts =0for j in range(i +1, n):
dx = points[j][0] - points[i][0]
dy = points[j][1] - points[i][1]
if dx ==0and dy ==0:
duplicate +=1continue# Simplify the slope using GCD g = gcd(dx, dy)
dx //= g
dy //= g
slope =f"{dy}/{dx}"# Represent the slope as a string to handle precision slope_count[slope] +=1 max_pts = max(max_pts, slope_count[slope])
ans = max(ans, max_pts + duplicate +1) # Including duplicates and the current pointreturn ans