题目:Max Points on a Line
找到给定的点集中在一条直线上的最大点数。
思路:
两点确定一条直线,可以通过y=k*x+b中的k和b来确定直线;
k=(y1-y0)/(x1-x0) b=(x1*y0-x0*y1)/(x1-x0);
所以用double类型存储k,b;
遍历每个点找到k,b相同的直线。
注意:
可能有相同坐标的点和横坐标相同(k为无穷大)的点。
int maxPoints(vector<Point>& points) { if (points.size() < 3)return points.size(); int max = 2; double MAX_DOUBLE = 1.7E308; double MIN_DOUBLE = 1.0E-100; auto it = points.cbegin(); while (it != points.cend()){ //cur统计it的每个点的对应的直线上的最大点数量 int cur = 1,equalCount = 0;//equalCount统计与it对应的点相同的点数量。 auto p = it + 1; unordered_map<double,pair<double,int>>arr; while (p != points.cend()){ double a = (p->x - it->x); double k = (p->y - it->y); double b = 0.0; if (a < MIN_DOUBLE && a > -MIN_DOUBLE){ if (k < MIN_DOUBLE && k > -MIN_DOUBLE){//两个点相同 ++equalCount; ++p; continue; } else{//横坐标相等 k = MAX_DOUBLE; b = MAX_DOUBLE; } } else{//其他情况 k = k / a; b = (p->x*it->y - p->y*it->x)*1.0 / a; } auto pos = arr.find(k); int count = 2; if (pos == arr.cend())arr[k] = make_pair(b,2);//没有同一条直线的点 else if (pos->second.first - b > MIN_DOUBLE || pos->second.first - b < -MIN_DOUBLE){//k相同,b不同 arr[k] = make_pair(b, 2); } else{//在同一条直线上 count = ++(pos->second.second); } if (count > cur)cur = count; ++p; } cur += equalCount; if (cur > max)max = cur; ++it; } return max; }
但是上面的算法不能通过所有的测试用例,因为double的精度损失,导致无法区分不同的k值;
所以考虑用int比较。
a*y = k*x + b;其中a = x1 - x0;k = y1 - y0;b = x1*y0 - x0*y1;
是不是一定需要这三个数呢?
b = x1*y0 - x0*y1 = (x1 - x0)*y0 - (y1 - y0)*x0 = a*y0 - k*x0 = a*y1 - k*x1;
由于循环比较的时候有一个点是固定不变的,所以上面的b也是固定的,它是否相等,完全取决于a和k;
于是只需要比较两个元素a和k,但是a,k还需要除以他们的最大公因数,因此需要一个求最大公因数的函数;
同时,也要考虑:坐标相同的点和横坐标相等的点。
int LeetCode::maxComDivisor(int a, int b){ if (b)return maxComDivisor(b,a%b); else return a; } int LeetCode::maxPoints(vector<Point>& points){ if (points.size() < 3)return points.size(); int max = 2; auto it = points.cbegin(); while (it != points.cend()){ //cur统计it的每个点的对应的直线上的最大点数量;equalCount统计与it对应的点相同的点数量,vertical统计垂直的线的个数。 int cur = 1,equalCount = 0,vertical = 1; auto p = it + 1; map<pair<int,int>,int>arr; while (p != points.cend()){ int a = p->x - it->x; int k = p->y - it->y; int b = p->x*it->y - p->y*it->x; if (!a){ if (!k){ ++equalCount; ++p; continue; } else{//横坐标相同的点 ++vertical; if (vertical > cur)cur = vertical; } } else{ int mcd = maxComDivisor(a, k);//求最大公约数 a = a / mcd; k = k / mcd; pair<int, int> temp(a, k); auto pos = arr.find(temp); int count = 2; //没有一条直线的点,则加入map if (pos == arr.cend())arr[temp] = 2; else{//有一条直线的点 count = ++(pos->second); } if (count > cur)cur = count; } ++p; } cur += equalCount;//加上与当前相同的点的数量 if (cur > max)max = cur; ++it; } return max; }