Given n points on a 2D plane, find the maximum number of points that lie on the same straight line.
注意题目中给的点有可能是重复的。要统计哪条直线上的点最多,就一定要计算每两个点之间的直线,这是不可避免的,所以时间复杂度至少为O(n^2)。采用遍历的方法,当遍历第i个点时,只需计算它与第i+1个及之后的点的直线,这样避免了重复计算。
我最初的想法是用y=kx+b表示直线方程,然后记录每个(k,b)组合出现的次数,出现最多的那个组合表示的直线方程就是经过点最多的。出现次数可以用map<pair<double,double>, int>类型记录。这个方法有两个问题:第一,k表示斜率,但不是每两个点间的直线斜率都存在,所有垂直于x轴的直线都没有斜率,所以可能会出现被0除的错误;第二,题目给出的点坐标都是整数,计算k时会出现精度丢失的问题,可能出现两条斜率相近的直线被误认为相同的情况。
因为k=(y2-y1)/(x2-x1),为了解决上述两个问题,我不用k表示直线,而是直接用dy=y2-y1和dx=x2-x1表示。但是这样就要用dy、dx、b三个参数表示方程了,不能用map<pair, int>的数据结构了。b=y-kx,是由直线上的一个点和斜率决定的。如果我们在遍历每一个点时,都新建一个map记录每个(dy,dx)组合出现的次数,就可以省略b了,因为每条直线都会经过当前遍历的点,所以只要直线的dy、dx相同,它们的b值也必定相同。这样,在每个点遍历结束时,选出经过当前点的直线中包含点最多的那条,最后再在所有点的最值中选出包含点最多的直线。
/**
* Definition for a point.
* struct Point {
* int x;
* int y;
* Point() : x(0), y(0) {}
* Point(int a, int b) : x(a), y(b) {}
* };
*/
class Solution {
public:
int maxPoints(vector<Point>& points) {
int nums = points.size();
int x,y,gcd,duplicate;
pair<int, int> p;
int maxslope, maxline=0;
for(int i=0; i<nums; i++) {
duplicate = 1;
maxslope = 0;
map<pair<int,int>, int> count;
for(int j=i+1; j<nums; j++) {
y = points[j].y-points[i].y;
x = points[j].x-points[i].x;
if(x==0&&y==0) {
duplicate++;
}
else {
gcd = GCD(x,y);
x = x/gcd;
y = y/gcd;
p.first = x;
p.second = y;
count[p]++;
maxslope = max(maxslope, count[p]);
}
}
maxline=max(maxline, maxslope+duplicate);
}
return maxline;
}
private:
int GCD(int a, int b) {
if(b==0) return a;
else return GCD(b, a%b);
}
};
注意dx、dy(代码中的x、y)需要先用它们的最大公约数去除,再去map中查找。因为例如(1,1)、(2,2)、(3,3)三个点在同一直线上,(1,1)、(2,2)计算出的(dx,dy)是(1,1),(1,1)、(3,3)计算出的(dx,dy)是(2,2),如果不进行约分,就会认为这两条直线是不同的。