Max Points on a Line

来源:互联网 发布:js 判断函数是否定义 编辑:程序博客网 时间:2024/05/05 03:02

https://oj.leetcode.com/problems/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.

public int maxPoints(Point[] points)


一条直线的表达方式有好几种,一般比较常见的就是y = kx + c 或者ax + by + c = 0

一种O(N^2)的做法是基于某一个起始点,然后和其他点进行比较,对于每一个起始点,我们维护一个哈希表,键放的东西是用来表达斜率k或者a和b,因为都是同一个起始点,所以起始就完全不需要考虑作为偏移量的常量C了。所以简单来说,在这种做法里,因为每两个点都可以得到一个k或者a和b,在不考虑偏移量的情况下,斜率相同就可以被认为是同一条直线上的。然后遍历所有点作为起始点就可以了。


基于这个算法,需要解决的就是如何在哈希表里去表达这个斜率了。每一个起始点都有自己的哈希表,键放的是斜率的表达方式,值放的就是一个结果数量。在这个过程里找到最大的结果数量并返回就可以。如上面所说,表达式有两种, y = kx + c,或者 ax + by + c = 0。 c我们已经不用管了。那么就有起码两种解决方式了。


第一种,键值那里放的是一个k,double值。这个k的求值方法为(y2 - y1)/(x2 - x1),这里要处理的边界情况有两个,首先显而易见的是x2 == x1的情况。所以我们除了哈希表以外,还需要一个临时变量去处理这个,然后第二个边界情况是不那么显而易见的,那就是y2 == y1的情况。事实上本身这个情况是不那么需要处理的。但是在Java里(c++里没验证过),Double值0和-0是不一样的。所以即使y2 == y1,x2 > x1和x1 > x2时上述表达式求出的斜率是不同的。。这样就错了。所以这也是一个特殊情况。还有一个最特殊的情况就是重复点。判断完这三个特殊情况就可以进行正常的判断了。给出代码如下:

    public int maxPoints(Point[] points) {        int res = 0;        for(int i = 0; i < points.length; i++){            HashMap<Double, Integer> k_counter = new HashMap<Double, Integer>();            int same_x = 0, same_y = 0, same_point = 1;            int cur_res = 0;            for(int j = i + 1; j < points.length; j++){                if(points[j].x == points[i].x && points[i].y == points[j].y){                    same_point++;                }else if(points[j].x == points[i].x){                    same_x++;                }else if(points[j].y == points[i].y){                    same_y++;// same_y 必须要的原因是在double里,0.0 和 -0.0是不一样的。所以Wrong Answer最后一个case无法过就是如此。                }else{                    Double k = (points[j].y - points[i].y) / (double)(points[j].x - points[i].x);                    k_counter.put(k, k_counter.containsKey(k) ? k_counter.get(k) + 1 : 1);                    cur_res = Math.max(cur_res, k_counter.get(k));                }            }            res = Math.max(res, Math.max(cur_res, Math.max(same_x, same_y)) + same_point);        }        return res;    }

第二种就是用一个String来表达ax + by + c = 0中的a和b。这种情况会比上一个做法少一点边界情况的处理,但并不代表不需要处理。

根据表达式的求值方法,ax + by也可以表达为(y1 - y2)x + (x2 - x1)y。也就是实际上我们用y1 - y2和x1 - x2两个值来替换一个k的表达。可是这里我们再来看看一个情况。

x + 2y + 3 = 0 和 2x + 4y + 6 = 0其实是在表达同一条直线,只是第二个公式还需要化简才能转换成第一个表达式。所以实际上我们还需要得到y1 - y2 和 x1 - x2的最大公约数,然后将y1 - y2和x1 - x2同时除以这个公约数,再存放才具备参考价值。关于公约数的求法,参见代码吧。

    public int gcd(int a, int b){//这个就是求最大公约数的函数        return (a != 0) ? gcd(b % a, a) : b;    }        public int maxPoints(Point[] points) {        int result = 0;        for(int i = 0; i < points.length; i++){            HashMap<String, Integer> map = new HashMap<String, Integer>();            int same_points = 1;            int curmax = 0;            for(int j = i + 1; j < points.length; j++){                if(points[i].x == points[j].x && points[i].y == points[j].y){                    same_points++;                }else{                    int diffx = points[i].x - points[j].x;                    int diffy = points[i].y - points[j].y;                    int gcd = gcd(diffx, diffy);                    if(diffx * gcd < 0)                        gcd *= -1;                    diffx = gcd == 0 ? diffx : diffx / gcd;                    diffy = gcd == 0 ? diffy : diffy / gcd;                    String key = diffx + " " + diffy;                    if(map.containsKey(key)){                        int cur_line = map.get(key) + 1;                        curmax = Math.max(cur_line, curmax);                        map.put(key, cur_line);                    }else{                        map.put(key, 1);                        curmax = Math.max(curmax, 1);                    }                }            }            result = Math.max(result, curmax + same_points);        }        return result;    }


0 0