leetcode 149 直线上最多的点数

许久未发题解了,前阵子忙于期末考。

今天想说一道有意思的题目,偏向于数学的编程题:leetcode 149 直线上最多的点数。原题链接https://leetcode-cn.com/problems/max-points-on-a-line/

题目是这样的,给你一个二维数组,每个数组中都是一个二维坐标,就是数学中的x与y,所以points[n]的size一定是2。每个坐标代表着一个点,问你最多有几个点在同一条直线上。

是不是觉得题目通俗易懂却又不知道用什么方法来做?其实这道题没有用到任何的算法,只是纯粹靠数学认知来解。我们可以把直线上最多点的问题看成N个点是否在一条直线上,这样的话就可以用斜率来表示。

首先需要注意几点:
1)斜率为0的时候怎么办?
2)分母为0怎么表示?
3)float类型或者double类型不能精确表示分数,如果斜率为1/3等情况怎么表示?
4)2/4斜率与1/2斜率一样,我们如何判断?
5)对于负数的坐标应当如何处理?
6)如果多个坐标在同一个点应当怎么办?例如 [[1,1],[1,1],[1,1],[2,2]]

那么先附上我的代码

class Solution {
public:
    int maxPoints(vector<vector<int>>& points) {
        if(points.size()<=1)
            return points.size();
        int ret=1;
        for(int i=0;i<points.size();i++){
            map<vector<int>,int> a;
            int same=0;
            for(int j=0;j<points.size();j++){
                if(points[i][0]==points[j][0]&&points[i][1]==points[j][1])
                    same++;
                else
                    a[slope(points[j],points[i])]++;
            }
            ret=max(ret,same);
            for(map<vector<int>,int>::iterator it=a.begin();it!=a.end();it++)
            	ret=max(ret,same+it->second);
        }
        return ret;
    }
    vector<int> slope(vector<int>& pa,vector<int>& pb){
        int dy=pa[1]-pb[1];
        int dx=pa[0]-pb[0];
        if(dx==0)
            return {1,0};
        if(dy==0)
            return {0,1};
        int g=gcd(abs(dy),abs(dx));
        dx/=g;
        dy/=g;
        if(dx<0){
            dx=-dx;
            dy=-dy;
        }
        return {dy,dx};
    }

    int gcd(int a,int b){
        if(a<b)
            swap(a,b);
        if(a%b==0)
            return b;
        return gcd(b,a%b);
    }
};

为了避免3)这种情况的发生,我们将斜率表示成一种分数的形式,同时为了解决4)这个问题,将这个分数化简成一个最简的形式,例如2/4斜率与1/2斜率一样,我们都将它统一成1/2,返回值的话其实就是一个vector,以[1,2]这样的形式返回。这个时候我们就需要找出分子分母的最大公约数

上面的gcd函数就是求两个数的最大公约数,使用的是辗转相除法,是一种递归的概念。先保证a比b大,如果b能被a整除,那么b就是最大公约数。如果不是的话,那么就继续调用这个函数,递归下去,不过里面的实参会改变。看不懂的没有关系,这个方法是一个定理,直接记住就好了。

然后的话就可以说一下slope这个函数,slope返回的是一个vector类型的数组。它的功能呢就是传入两个点,将它"最简的分数形式"进行返回。在数学中,求斜率的公式就是dy/dx,例如我们传入的是[1,4],[2,8]这两个点,dx=2-1=1,dy=8-4=4,那么这个点的斜率就是4/1=4,不过我们为了避免3)这种情况,我们直接返回[4,1]就好了,这个方法就是解出这道题的关键。对于dx=0的情况,我们返回[1,0],对于dy=0的情况,我们返回[0,1]就好了,这样就可以解决1)和2)这两个疑问。
后面的话我们求出dx与dy的最小公约数,并让dx与dy除以这个最小公约数获得最简形式。当然我们得保证求最小公约数时传入的数是正数,所以如果dx<0的话,我们就让dx=-dx, dy=-dy。例如dx=-2,dy=-4,它的斜率为一个正数,在求最小公约数的时候,求出来的最小公约数为2,不过由于dx<0,我们应当返回[2,1]。
相信到了这里,你们已经清楚了这道题的主要思路了吧:)

最后在maxPoints这个函数中,我们只需要枚举出所有的点,依次以这个点为边的一个端点来求出它的斜率,因为以一个固定的点画出一条斜率为k的直线只会有一条!这时我们用map来记录下斜率为k出现的次数,然后作出统计。值得一提的是,之前说的6)这种情况: [[1,1],[1,1],[1,1],[2,2]],我们直接可以看这个x与我们枚举这个点的x是否相同&&y与我们枚举这个点的y是否相同,如果是的话那么就是同一个点,我们只需要在最后统计斜率的时候加上同一个点出现的次数就好了。当然还有一种情况,例如: [[1,1],[1,1],[1,1],[1,1]]这时我们的map为空,我们无法使用map的迭代器来执行到ret=max(ret,same+it->second)这句话,这样的话我们统计相同的点same就无法return了,所以我在迭代器之前加入了ret=max(ret,same)来解决这种特殊情况的发生却无法统计same的情景

这些都是这段代码中细节处理的地方。如果细节处理不好,虽然可以通过大部分测试用例,不过在一些极端的情况下,就会产生一些意想不到的bug。

继续加油:)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值