这是LeetCode上的一道题,题目如下:
Given n points on a 2D plane, find the maximum number of points that lie on the same straight line.
看到这道题目,首先想到判断点是否在直线上,我们会想到依靠斜率是否相等来判断。但是还存在一个问题,比如有四个点A(x1,y1),B(x2,y2),C(x3,y3),D(x4,y4),如果A,B形成直线的斜率等于C,D形成直线的斜率,那么我们是不能认为A,B,C,D四个点是在同一条直线上的,比如A(0,0),B(1,1),C(1,0),D(2,1),这是因为不同直线的截距不同,所以仅仅这样判断不行,那么我们要求解这道问题,难道还要记录截距吗?那么计算过程就复杂了,能不能只用斜率就行呢?答案是肯定的,我们观察到对于同一个点,比如点A,如果B,C,D中有点与A形成的直线斜率相等,那么必然他们就都在同一条直线上,因为两点确定一条直线,则一点固定,之后如果斜率相等,必然都是同一条直线,那么我们就可以通过这个规律对这个数组进行两次扫描得到最终的结果。
我觉得这道题并不难,最主要是考查我们对特殊情况的处理,首先考虑直线,两点形成一条直线,但是可能存在:
1.直线斜率不存在 2.直线斜率为0 3.对于重复元素的处理(我开始没有考虑到有这个问题,当有一个有重复元素的测试用例没有通过时发现此问题)。这三个问题都解决了之后基本就能解出这个问题。
另外,我写算法的过程中如果求最大值需要先对数组进行扫描并且有中间结果存储的时候,通常会先把所有的中间结果计算出来,然后再在中间结果中找到最大的,其实不必要这样,可以直接用临时变量一直保存最大的即可,中间结果就不需要了。
下面把我的代码放在下面,需要特别注意的地方注释标明:
class Solution { public: //判断点是否在直线上,就是判断斜率是否相同 int maxPoints(vector<Point> &points) { int maxLine = 0; if(points.size() <=2) return points.size(); for (int i=0; i<points.size()-1; i++) { int coincident = 0; int h = 0; map<double, int> pointCounts; map<double, int>::iterator it; for (int j=i+1; j<points.size(); j++) { double slope; if (points[i].x==points[j].x && points[i].y==points[j].y) { coincident++;//考虑重复元素,直接在每一条包含该点的直线上都加1 continue; } else if (points[i].x == points[j].x) { h++;//对于斜率不存在的情况,特殊处理 continue; } else if (points[i].y == points[j].y) { slope = 0.0; // double类型分正0与负0,在计算机内的表示不一样,故需要在此特别标明 } else { slope = (double)(points[i].y-points[j].y) / (points[i].x-points[j].x); } it = pointCounts.find(slope); if (it!=pointCounts.end()) it->second +=1; else pointCounts.insert(pair<double,int>(slope,1)); } coincident++;//将索引为i的元素计算在内 h = h + coincident;//计算重复元素 if(maxLine < h) maxLine = h; for(it = pointCounts.begin();it!=pointCounts.end();it++){ if(maxLine < (it->second+coincident)){ maxLine = it->second+coincident; } } } return maxLine; } };