原题:
Given n points on a 2D plane, find the maximum number of points that lie on the same straight line.
在一个二维平面上有一个点,求这些点组成的直线中含有最多的点的点的个数。
思路:这个题思路不难,第一步首先找出所有的直线,放到一个HashSet中,使用HashSet是为了去重。第二步遍历所有的直线,每条直线都遍历一遍所有的点,找出在这条直线上的点,并计数。算法的时间复杂度为O(n^2)。
但是在实现的时候,需要注意两个问题,一是如何表示一条直线,二是float/double等的精度问题。
关于如何表示一条直线,我们知道y=ax+b,参数a和b唯一确定一条直线,那就用a和b来表示一条直线。第二个精度问题,我们知道a是斜率,是由除法计算得来的,由于浮点数存在精度问题,我们在验证一个点Point p在不在这直线的时候,把p的坐标带入到y=ax+b时,很容易出现原来本该相等却不相等的情况。这就提示我们不能用浮点数,应该用分数,并且在计算过程中不能出现除法。
因此直线用y=(a1/a2)*x+(b1/b2)来表示,即用分子分母互质的分数来代替浮点数。带入一个点验证在不在直线上的时候也不能用除法,整理直线公式得y*a2*b2=a1*b2*x+b1*a2的时候在直线上。
另外因为我们用hashSet来保存直线,所以需要重写equals和hashcode方法。
代码如下:
点的表示
class Point {
int x;int y;
Point() {x = 0;y = 0;}
Point(int a, int b) {x = a;y = b;}
}
直线的表示:
class Line {
int a1,a2,b1,b2;
boolean vertical; //垂直直线
boolean level; //水平直线
public Line(int a1, int a2, int b1,int b2,boolean v, boolean l) {
this.a1 = a1;
this.a2 = a2;
this.b1 = b1;
this.b2 = b2;
this.vertical = v;
this.level = l;
}
}
//这里略去equals和hashcode方法的重写。
主函数:
public int maxPoints(Point[] points) {
if (points == null || points.length == 0) {
return 0;
} else if (points.length == 1) {
return 1;
}
HashSet<Line> set = new HashSet<Line>();
//计算出所有的直线,并保存
for (int i = 0; i < points.length; i++) {
for (int j = i + 1; j < points.length; j++) {
set.add(genLine(points[i], points[j]));
}
}
int maxNum = Integer.MIN_VALUE;
for (Line l : set) {
int count = 0;
for (int i = 0; i < points.length; i++) {
if (onLine(l, points[i])) {
count++;
}
}
if (count > maxNum) {
maxNum = count;
}
}
return maxNum;
}
//判断点在不在直线上
public boolean onLine(Line l, Point p) {
if (l.level) {
if(p.y==l.b1){return true;}
return false;
}
if (l.vertical) {
if(p.x==l.b1){return true;}
return false;
}
int left = p.y*l.a2*l.b2;
int right = l.a1*p.x*l.b2+l.b1*l.a2;
if(left==right){
return true;
}
return false;
}
//两点产生一条直线
public Line genLine(Point p1, Point p2) {
if (p1.x == p2.x) { // 垂直的
return new Line(0, 0, p1.x,0,true, false);
}
if (p1.y == p2.y) {
return new Line(0, 0, p1.y,0,false, true);
}
int a1 = p1.y-p2.y;
int a2 = p1.x-p2.x;
int ga = gcd(Math.max(a1, a2),Math.min(a1, a2));
a1 /= ga;
a2 /= ga;
int b1 = a2*p1.y-a1*p1.x;
int b2 = a2;
int gb = gcd(Math.max(b1, b2),Math.min(b1, b2));
b1 /= gb;
b2 /= gb;
return new Line(a1, a2, b1,b2, false, false);
}
//欧几里德算法计算公约数
public int gcd(int a, int b) {
if (b == 0) {
return a;
}
return gcd(b, a % b);
}