题目: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;
}