149. 直线上最多的点数

给定一个二维平面,平面上有 n 个点,求最多有多少个点在同一条直线上。

示例 1:

输入: [[1,1],[2,2],[3,3]]
输出: 3
解释:
^
|
| o
| o
| o
+------------->
0 1 2 3 4
示例 2:

输入: [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]]
输出: 4
解释:
^
|
| o
| o o
| o
| o o
+------------------->
0 1 2 3 4 5 6

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/max-points-on-a-line
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

参考:https://leetcode.wang/leetcode-149-Max-Points-on-a-Line.html?q=

两点确定一条直线,最简单的方式考虑任意两点组成一条直线,然后判断其他点在不在这条直线上。

两点确定一条直线,直线方程:

image.png

等式转换:

image.png

但是如果乘积过大,会造成内存溢出

所以引入 欧几里德算法 欧几里得算法又称辗转相除法,是指用于计算两个非负整数a,b的最大公约数。应用领域有数学和计算机两个方面。计算公式gcd(a,b) = gcd(b,a mod b)。

private int gcd(int a, int b) {
    while (b != 0) {
        int temp = a % b;
        a = b;
        b = temp;
    }
    return a;
}

暴力解法

public int maxPoints(int[][] points) {
        if (points.length < 3) {
            return points.length;
        }

        //如果两个点一直相等  判断所有点是否都相同的特殊情况
        int index = 0;
        for (; index < points.length - 1; index++) {
            if (points[index][0] == points[index + 1][0] && points[index][1] == points[index + 1][1]) {
                continue;
            }
            break;
        }

        if (index == points.length - 1) {
            return points.length;
        }

        int max = 0;

        for (int i = 0; i < points.length; i++) {

            for (int j = 1; j < points.length; j++) {

                int tempMax = 0;

                if (points[i][0] == points[j][0] && points[i][1] == points[j][1]) {
                    continue;
                }

                for (int k = 0; k < points.length; k++) {

                    if (k == i || k == j) {
                        continue;
                    }

                    if (line(points[i][1], points[i][0], points[j][1], points[j][0], points[k][1], points[k][0])) {
                        tempMax++;
                    }
                }

                max = Math.max(tempMax, max);

            }
        }
        //加上直线本身的两个点
        return max + 2;
    }

    /**
     * y2-y1            y-y2
     * -----     ==   ------
     * x2-x1            x-x2
     * <p>
     * <p>
     * 斜率一样
     *
     * @return
     */
    private boolean line(int y1, int x1, int y2, int x2, int y, int x) {

        int gcd1 = gcd(y2 - y1, x2 - x1);

        if (y == y2 && x == x2) {
            return true;
        }

        int gcd2 = gcd(y - y2, x - x2);

        return (y2 - y1) / gcd1 == (y - y2) / gcd2 && (x2 - x1) / gcd1 == (x - x2) / gcd2;
    }


    /**
     * 求最大公约数
     */
    private int gcd(int a, int b) {
        while (b != 0) {
            int temp = a % b;
            a = b;
            b = temp;
        }
        return a;
    }

Map 解法

回到数学上,给定两个点可以唯一的确定一条直线,表达式为 y = kx + b。

public int maxPoints(int[][] points) {
        if (points.length < 3) {
            return points.length;
        }

        //如果两个点一直相等
        int index = 0;
        for (; index < points.length - 1; index++) {
            if (points[index][0] == points[index + 1][0] && points[index][1] == points[index + 1][1]) {
                continue;
            }
            break;
        }

        if (index == points.length - 1) {
            return points.length;
        }

        int res = 0;
        //遍历每个点
        for (int i = 0; i < points.length; i++) {
            int duplicate = 0;
            int max = 0;//保存经过当前点的直线中,最多的点
            HashMap<String, Integer> map = new HashMap<>();
            for (int j = i + 1; j < points.length; j++) {
                //求出分子分母
                int x = points[j][0] - points[i][0];
                int y = points[j][1] - points[i][1];
                if (x == 0 && y == 0) {
                    duplicate++;
                    continue;

                }
                //进行约分
                int gcd = gcd(x, y);
                x = x / gcd;
                y = y / gcd;
                String key = x + "@" + y;
                map.put(key, map.getOrDefault(key, 0) + 1);
                max = Math.max(max, map.get(key));
            }
            //1 代表当前考虑的点,duplicate 代表和当前的点重复的点
            res = Math.max(res, max + duplicate + 1);
        }
        return res;
    }


    /**
     * 求最大公约数
     */
    private int gcd(int a, int b) {
        while (b != 0) {
            int temp = a % b;
            a = b;
            b = temp;
        }
        return a;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值