Given n points on a 2D plane, find the maximum number of points that lie on the same straight line.
Brute Force的做法,N个点两两可以构成N(N-1)/2条线,我们可以找这N(N-1)/2条线中线上点数最大值,只需对每一条线再进行一层O(N)的遍历,总共是O(N^3)。
用第二种方法更好,选一个基准点, 看后面每一个点跟它构成的直线, 维护一个HashMap, key是跟这个点构成直线的斜率的值, 而value就是该斜率对应的点的数量, 计算它的斜率, 如果已经存在, 那么就多添加一个点, 否则创建新的key。 这里只需要考虑斜率而不用考虑截距是因为所有点都是对应于一个参考点构成的直线, 只要斜率相同就必然在同一直线上。 最后取map中最大的值, 就是通过这个点的所有直线上最多的点的数量。 对于每一个点都做一次这种计算, 并且后面的点不需要看扫描过的点的情况了, 因为如果这条直线是包含最多点的直线并且包含前面的点, 那么前面的点肯定统计过这条直线了。 因此算法总共需要两层循环, 外层进行点的迭代, 内层扫描剩下的点进行统计, 时间复杂度是O(n^2), 空间复杂度是哈希表的大小, 也就是O(n), 比起上一种做法用这里用哈希表空间省去了一个量级的时间复杂度。
First, let's talk about mathematics.
How to determine if three points are on the same line?
The answer is to see if slopes of arbitrary two pairs are the same.
Second, let's see what the minimum time complexity can be.
Definitely, O(n^2). It's because you have to calculate all slopes between any two points.
Then let's go back to the solution of this problem.
In order to make this discussion simpler, let's pick a random point A as an example.
Given point A, we need to calculate all slopes between A and other points. There will be three cases:
-
Some other point is the same as point A.
-
Some other point has the same x coordinate as point A, which will result to a positive infinite slope.
-
General case. We can calculate slope.
We can store all slopes in a hash table. And we find which slope shows up mostly. Then add the number of same points to it. Then we know the maximum number of points on the same line for point A.
We can do the same thing to point B, point C...
Finally, just return the maximum result among point A, point B, point C...
gcd
/* * A line is determined by two factors,say y=ax+b * * If two points(x1,y1) (x2,y2) are on the same line(Of course). * Consider the gap between two points. * We have (y2-y1)=a(x2-x1),a=(y2-y1)/(x2-x1) a is a rational, b is canceled since b is a constant * If a third point (x3,y3) are on the same line. So we must have y3=ax3+b * Thus,(y3-y1)/(x3-x1)=(y2-y1)/(x2-x1)=a * Since a is a rational, there exists y0 and x0, y0/x0=(y3-y1)/(x3-x1)=(y2-y1)/(x2-x1)=a * So we can use y0&x0 to track a line; */ public class Solution{ public int maxPoints(Point[] points) { if (points==null) return 0; if (points.length<=2) return points.length; HashMap<String, Integer> map = new HashMap<>(); int max = 1; for (int i = 0; i < points.length; i++) { map.clear(); int dup = 0; for (int j = i + 1; j < points.length; j++) { if (points[i].x == points[j].x && points[i].y == points[j].y) { dup++; continue; } int xDiff = points[i].x - points[j].x; int yDiff = points[i].y - points[j].y; String key; if (xDiff == 0 || yDiff == 0) { key = getKey(xDiff, yDiff); } else { int gcd = getGCD(xDiff, yDiff); xDiff /= gcd; yDiff /= gcd; key = getKey(xDiff, yDiff); } System.out.println("key: " + key); if (map.containsKey(key)) { map.put(key, map.get(key + 1)); } else { map.put(key, 2); } } for (int amount : map.values()) { max = Math.max(max, amount + dup); } } return max; } public static int getGCD(int x, int y) { if (y == 0) { return x; } return getGCD(y, x % y); } public static String getKey(int x, int y) { if (x == 0) { return "INF"; } if (y == 0) { return "0"; } return x + "+" + y; } public static void main(String[] args) { Point[] pp = new Point[10]; for (int i = 10; i >= 1; i--) { pp[i - 1] = new Point(i, i); } //Point center = new Point(0, 0); //System.out.println(findPoints(5, center, pp)); //System.out.println(quickFindPoints(5, center, pp)); Point[] points = {new Point(0, 0), new Point(-1, -1), new Point(2, 2)}; System.out.println(maxPoints(points)); }
double
/** * Definition for a point. * class Point { * int x; * int y; * Point() { x = 0; y = 0; } * Point(int a, int b) { x = a; y = b; } * } */ public class Solution { public int maxPoints(Point[] points) { if (points==null || points.length==0) return 0; int allTimeMax = 0; for (int i=0; i<points.length; i++) { HashMap<Double, Integer> map = new HashMap<Double, Integer>(); double ratio = 0.0; int sameNum = 0; int localMax = 1; for (int j=i+1; j<points.length; j++) { if (points[j].x == points[i].x && points[j].y == points[i].y) { sameNum++; continue; } else if (points[j].x == points[i].x) { ratio = (double)Integer.MAX_VALUE; } else if (points[j].y == points[i].y) { ratio = 0.0; } else { ratio = (double)(points[j].y - points[i].y) / (double)(points[j].x - points[i].x); } if (map.containsKey(ratio)) { map.put(ratio, map.get(ratio)+1); } else { map.put(ratio, 2); } } for (int value : map.values()) { localMax = Math.max(localMax, value); } localMax = localMax + sameNum; allTimeMax = Math.max(allTimeMax, localMax); } return allTimeMax; } }
一些细节需要注意:第40-44行用一个localMax以及allTimeMax是有深意的,我开始的时候只维护一个allTimeMax,出错了,不适用于input都是重复点这种情况【0,0】【0,0】这种情况。这种情况下map为空,sameNum为1。map.values()这个循环直接略过,如果只有一个allTimeMax将无法给它赋值
for (int num : map.values()) {
maxpts = Math.max(maxpts, num+dup);
}