题目
给定一个二维平面,平面上有 n 个点,求最多有多少个点在同一条直线上。
示例 1:
输入: [[1,1],[2,2],[3,3]]
输出: 3
解释:
^
|
| o
| o
| o
+------------->
0 1 2 3 4
示例 2:
输入: [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]]
输出: 4
解释:
^
|
| o
| o o
| o
| o o
+------------------->
0 1 2 3 4 5 6
思路1
遍历确定第一个点,遍历确定第二个点,这两点确定一条直线。遍历第三个点是否在这条直线上。
代码1
/**
* Definition for a point.
* class Point {
* int x;
* int y;
* Point() { x = 0; y = 0; }
* Point(int a, int b) { x = a; y = b; }
* }
*/
class Solution {
public int maxPoints(Point[] points) {
if(points.length<=2){
return points.length;
}
int res=2;
// 第一个点
for(int i=0;i<points.length;i++){
boolean[] isused=new boolean[points.length];
isused[i]=true;
// 第二个点
for(int j=0;j<points.length;j++){
if(isused[j]){
continue;
}
int count=2;
isused[j]=true;
// 第三个点
for(int k=0;k<points.length;k++){
if(isused[k]){
continue;
}
if(isLine(points[i],points[j],points[k])){
count++;
if(res<count){
res=count;
}
isused[k]=true;
}else{
}
}
}
}
return res;
}
// 判断第三个点是否与前两个点在一条直线上
public static boolean isLine(Point p1,Point p2,Point p3){
if(p3.x==p1.x && p3.y==p1.y){
return true;
}
if(p3.x==p2.x && p3.y==p2.y){
return true;
}
if(p1.x==p2.x){
return p3.x==p1.x;
}
if(p3.x==p1.x || p3.x==p2.x){
return false;
}
// 注意精度问题,例如三个点 (0,0) (94911150,94911151) (94911151,94911152)
// (double)94911150/(double)94911151的值与(double)94911151/(double)94911152的值竟然是相等的。
// if((double)(p1.y-p2.y)/(double)(p1.x-p2.x)==(double)(p3.y-p2.y)/(double)(p3.x-p2.x)){
// return true;
// }else{
// return false;
// }
// 为解决此问题,可使用BigDecimal解决精度问题,但BigDecimal不让使用
// 被除数.divide(除数,小数点后保留几位,四舍五入).toString();
// String b1=new BigDecimal(p1.y-p2.y).divide(new BigDecimal(p1.x-p2.x), 25, RoundingMode.HALF_UP).toString();
// String b2=new BigDecimal(p3.y-p2.y).divide(new BigDecimal(p3.x-p2.x), 25, RoundingMode.HALF_UP).toString();
// if(b1.equals(b2)){
// return true;
// }else{
// return false;
// }
// 为解决此问题,加上 && (double)(p1.y-p2.y)/(double)(p1.x-p2.x)==(double)(p3.y-p1.y)/(double)(p3.x-p1.x),进行双重判断.
// 虽然能通过测试,但依然不是精确的表示
// if((double)(p1.y-p2.y)/(double)(p1.x-p2.x)==(double)(p3.y-p2.y)/(double)(p3.x-p2.x) && (double)(p1.y-p2.y)/(double)(p1.x-p2.x)==(double)(p3.y-p1.y)/(double)(p3.x-p1.x)){
// return true;
// }else{
// return false;
// }
// 为解决此问题,分子分母进行约分,化为最简形式比较
int gcd1=gcd(p1.y-p2.y,p1.x-p2.x);
int gcd2=gcd(p3.y-p2.y,p3.x-p2.x);
if( (p1.y-p2.y)/gcd1 == (p3.y-p2.y)/gcd2 && (p1.x-p2.x)/gcd1 == (p3.x-p2.x)/gcd2){
return true;
}else{
return false;
}
}
public static int gcd(int a,int b){
if(b==0){
return a;
}
return gcd(b,a%b);
}
}
注意
1.判断第三个点是否在前两个点所在的直线上时,即判断a/b==c/d时,注意精度问题(看代码注释)。正确的做法是,分子分母求最大公约数然后进行约分变为最简分数再比较,不能用简单的除法计算。
2.求最大公约数。见代码。
思路2
使用map更快。遍历确定第一个点,遍历第二个点与第一个点的斜率放入map<斜率,count>。