149. Max Points on a Line

原题:
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);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值