Given n points on a 2D plane, find the maximum number of points that lie on the same straight line.
分析:首先要注意的是,输入数组中可能有重复的点。由于两点确定一条直线,一个很直观的解法是计算每两个点形成的直线,然后把相同的直线合并,最后包含点最多的直线上点的个数就是本题的解。我们知道表示一条直线可以用斜率和截距两个浮点数(垂直于x轴的直线斜率为无穷大,截距用x截距),同时还需要保存每条直线上的点(避免重复)。听起来就很麻烦,但是基于这个思想有一种简单的实现方式:
- 以某点O为中心,计算它和其他点的斜率,如果有两个点A、B和O点形成的斜率相等,那么ABO三点是共线的,如果有多个点和O的斜率相等,那么这多个点和O也是共线的,因此我们可以求出包含O的所有直线中点数最多的直线,会得到一个最大共线点数k(O),如果和O点重复的点有n个(除了O本身),那么K(O) = K(O) + n。这一步的计算中,为了提高效率,我们可以用哈希表来保存某个斜率对应的点的数目。
- 对数组中的每一个点i,按照第一步计算得到每个点最大点数K(i)
- 从k(i)中选取最大的就是本题的解
- 注意:为了避免重复计算,以数组中第i个点为中心时,只计算数组中它右边的所有点
/**
* 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) {
// IMPORTANT: Please reset any member data you declared, as
// the same Solution instance will be reused for each test case.
int len = points.size(), res = 1;
if(len == 0)return 0;
unordered_map<double, int> umap;
for(int i = 0; i < len; i++)
{
umap.clear();
int samePointNum = 0,tmpres = 1;
for(int j = i+1; j < len; j++)
{
double slope = std::numeric_limits<double>::infinity();//斜率初始化为无穷大,我们视横坐标相同的两点间的斜率为无穷大
if(points[i].x != points[j].x)
slope = 1.0*(points[i].y - points[j].y) / (points[i].x - points[j].x);
else if(points[i].y == points[j].y)
{samePointNum++; continue;}
int tmp;
if(umap.find(slope) != umap.end())
tmp = ++umap[slope];
else
{
umap.insert(unordered_map<double, int>::value_type(slope, 2));
tmp = 2;
}
if(tmpres < tmp)tmpres = tmp;
}
if(res < tmpres + samePointNum)res = tmpres + samePointNum;
}
return res;
}
};
9.21更新
------------------
思路:
解这个平面几何题有3个要点:
1. 如何判断共线?
两点成一直线,所以两点没有共线不共线之说。对于点p1(x1, y1),p2(x2, y2),p3(x3, y3)来说,共线的条件是
p1-p2连线的斜率与p1-p3连线的斜率相同,即
(y2-y1)/(x2-x1) = (y3-y1)/(x3-x1)
所以对共线的n点,
其中任意两点连线的斜率相同。
2. 如何判断最多的共线点?
对于每个点p出发,计算该点到所有其他点qi的斜率,对每个斜率统计有多少个点符合。其中最多的个数加1(出发点本身)即为最多的共线点。
3. 特殊情况
当x1 = x2,y1!=y2时,为垂直连线。计算斜率时分母为0会出错。
当x1 = x2,y1 = y2时,两点重合。则(x2, y2)和所有(x1, y1)的连线共线。
此外,这道题还有一些trick,需要注意。
1. 对当前点遍历看有多少其他点跟他在一条线时,只遍历在它后面的点。这样是为了避免重复计算。
2. 利用hash map保存斜率的方式,来统计每个斜率有多少个点。
class Solution {
public:
int maxPoints(vector<Point> &points) {
int maxPts = 0;
for(int i=0; i<points.size(); i++) {
int nMax = 0, nSame = 0, nInf = 0;
unordered_map<float,int> comSlopes;
for(int j=i+1; j<points.size(); j++) {
if(points[j].x==points[i].x) {
if(points[j].y==points[i].y)
nSame++;
else
nInf++;
continue;
}
float slope = (float)(points[j].y-points[i].y)/(float)(points[j].x-points[i].x);
comSlopes[slope]++;
nMax = max(nMax, comSlopes[slope]);
}
nMax = max(nMax, nInf)+nSame+1;
maxPts = max(maxPts,nMax);
}
return maxPts;
}
};