1. 程式人生 > >精度-LeetCode149-直線上最多的點數

精度-LeetCode149-直線上最多的點數

題目

給定一個二維平面,平面上有 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>。