每日一题:直线上最多的点数

文章介绍了如何利用数学和编程解决在给定的X-Y平面上,找出最多有多少个点落在同一条直线上的问题,包括原始的思路代码和使用HashMap进行优化的方法。
摘要由CSDN通过智能技术生成

给你一个数组 points ,其中 points[i] = [xi, yi] 表示 X-Y 平面上的一个点。求最多有多少个点在同一条直线上。

示例 :

输入:points = [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]]
输出:4

一个点加一个斜率即可唯一的确定一条直线。

所以我们可以对「点」进行分类然后去求解,问题转换成,经过某个点的直线,哪条直线上的点最多。

纯思路代码,无优化:

class Solution {
    public int maxPoints(int[][] points) {
        int size = points.length;
        if(size <= 2){
            return size;
        }
        boolean[][] flag = new boolean[size][size];
        double[][][] kk = new double[size][size][size];
        int result = 0;
        for(int k = 0;k < size;k++){
            for(int i = 0;i < size;i++){
                if(points[k][0] != points[i][0]){
                    kk[k][i][0] = (points[k][1] - points[i][1]);
                    kk[k][i][1] = (points[k][0] - points[i][0]);
                }else{
                    flag[k][i] = true;
                }
            }
            for(int i = 0;i < size-1;i++){
                int temp = 0;
                if(flag[k][i] != true){
                    for(int j = 0;j < size;j++){
                        if((points[j][0] - points[k][0])*kk[k][i][0]/kk[k][i][1] == points[j][1] - points[k][1]){
                            temp++;
                        }
                    }
                }else{
                    for(int j = 0;j < size;j++){
                        if(points[j][0] == points[k][0]){
                            temp++;
                        }
                    }
                }
            
                if(temp > result){
                    result = temp;
                }
            
            }

        }
        return result;
    }
    
}

首先,检查给定的点集的大小。如果大小小于等于2,则直接返回该大小,因为至少需要两个点才能构成一条直线。

然后,创建布尔类型的二维数组flag和一个三维浮点数数组kk。flag用于标记是否存在斜率为无穷大的直线,而kk用于存储每对点之间的斜率。

接下来,使用两个嵌套循环来遍历所有点的组合。对于每一对点,它计算它们的斜率并将其存储在kk数组中。如果两点的x坐标相同,则将flag数组对应位置设置为true,表示存在斜率为无穷大的直线。

然后,使用另一个嵌套循环来计算每个点与其他点构成的直线上的点的数量。如果flag数组对应位置为false,则使用斜率公式计算其他点的y坐标,并与当前点的y坐标进行比较。如果相等,则说明该点与当前点在同一条直线上,将计数器temp加1。如果flag数组对应位置为true,则直接检查其他点的x坐标是否与当前点的x坐标相等,如果相等,则将计数器temp加1。

最后,更新结果变量result,使其保持最大的计数值。

注意,这里斜率可能是无穷小数,所以要注意存储斜率对应的分子、分母以及判断等式的构造。

 使用hashmap优化:

class Solution {
    public int maxPoints(int[][] points) {
        int n = points.length;
        if (n <= 2)return n;
        int result = 0;
        for (int i = 0; i < n; i++) {
            int temp = 0;
            Map<Integer, Integer> map = new HashMap<Integer, Integer>();
            if (result >= n - i || result > n / 2) {
                break;
            }
            for (int j = i + 1; j < n; j++) {
                int x = points[i][0] - points[j][0];
                int y = points[i][1] - points[j][1];
                if (x == 0) {
                    y = 1;
                } else if (y == 0) {
                    x = 1;
                } else {
                    if (y < 0) {
                        x = -x;
                        y = -y;
                    }
                    int gcd = gcd(Math.abs(x), Math.abs(y));
                    x = x / gcd;
                    y = y / gcd;
                }
                int key = y + x * 17;
                map.put(key, map.getOrDefault(key, 0) + 1);
            }
            
            for (Map.Entry<Integer, Integer> entry: map.entrySet()) {
                int num = entry.getValue();
                temp = Math.max(temp, num + 1);
            }
            result = Math.max(result, temp);
        }
        return result;
    }

    public int gcd(int a, int b) {
                if(a == 1 || b == 1)return 1;
        while(b > 0){
            int tmp = a;
            a = b;
            b = tmp % b;
        }
        return a;
    }
}

在内层循环中,计算当前点与后续点之间的x和y坐标差值。如果x或y为0,则将其设置为1,以避免除以0的情况。否则,根据x和y的正负关系,将它们转换为正数,并计算它们的最大公约数(gcd)。

然后,根据x和y的值计算出一个唯一的键值key,并将其存储在一个哈希表map中。键值的计算方式是将y加上17乘以x,这样可以保证不同的斜率对应不同的键值。

在外层循环结束后,遍历哈希表map中的每个条目,找到出现次数最多的斜率对应的点数,并将其加1后与temp比较取较大值。最后,将tempresult比较取较大值,更新result的值。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值