Leetcode【直线上最多的点数】

149. 直线上最多的点数

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

示例 1:

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

示例 2:

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

提示:

  • 1 <= points.length <= 300
  • points[i].length == 2
  • -104 <= xi, yi <= 104
  • points 中的所有点 互不相同

class Solution {
    public int maxPoints(int[][] points) {
        int n = points.length;
        // 如果点的数量小于等于2,则最多有n个点在同一条直线上
        if (n <= 2) {
            return n;
        }
        int ret = 0; // 记录最多的点数
        
        // 遍历每个点作为基准点
        for (int i = 0; i < n; i++) {
            // 如果当前找到的最大点数已经大于剩余点数或者大于总点数的一半,则可以提前终止
            if (ret >= n - i || ret > n / 2) {
                break;
            }
            // 使用HashMap记录每条直线上的点数
            Map<Integer, Integer> map = new HashMap<Integer, Integer>();
            
            // 遍历剩余的点来计算与基准点的直线斜率
            for (int j = i + 1; j < n; j++) {
                // 计算基准点和当前点的x和y差值
                int x = points[i][0] - points[j][0];
                int y = points[i][1] - points[j][1];
                
                // 处理特殊情况:如果x为0,则斜率为无穷大(垂直线)
                if (x == 0) {
                    y = 1; // 表示垂直线
                } 
                // 如果y为0,则斜率为0(水平线)
                else if (y == 0) {
                    x = 1; // 表示水平线
                } 
                // 计算斜率的最简形式
                else {
                    // 保持斜率的标准化形式:如果y为负,则同时取反x和y
                    if (y < 0) {
                        x = -x;
                        y = -y;
                    }
                    // 求x和y的最大公约数
                    int gcdXY = gcd(Math.abs(x), Math.abs(y));
                    x /= gcdXY; // 除以最大公约数
                    y /= gcdXY; // 除以最大公约数
                }
                
                // 将斜率转化为唯一的键(y + x * 20001),存储在map中
                // 20001是为了确保斜率的唯一表示(避免负值)
                int key = y + x * 20001;
                map.put(key, map.getOrDefault(key, 0) + 1);
            }
            
            // 找到当前基准点的最大点数
            int maxn = 0;
            for (Map.Entry<Integer, Integer> entry: map.entrySet()) {
                int num = entry.getValue();
                maxn = Math.max(maxn, num + 1); // +1是因为要包括基准点
            }
            
            // 更新最终的结果
            ret = Math.max(ret, maxn);
        }
        
        return ret;
    }

    // 求最大公约数(欧几里得算法)
    public int gcd(int a, int b) {
        return b != 0 ? gcd(b, a % b) : a;
    }
}

解题思路

  1. 基本情况处理

    • 如果点的总数小于等于2,直接返回点的数量,因为任何两点或单点都可以构成一条直线。
  2. 计算直线斜率

    • 对于每个点作为基准点,计算它与其他所有点的斜率。斜率的计算是通过两点之间的x和y坐标差值来实现的。
    • 处理斜率的特殊情况:
      • 如果x为0,则斜率为垂直线,可以用y = 1来表示。
      • 如果y为0,则斜率为水平线,可以用x = 1来表示。
    • 对于一般情况,计算斜率的最简形式,通过最大公约数化简斜率。
  3. 存储斜率信息

    • 使用 HashMap 存储每个斜率对应的点数。
    • 将斜率表示为一个唯一的键值对 y + x * 20001,其中 20001 是为了避免斜率表示中的负值冲突。
  4. 计算最大点数

    • 遍历所有斜率信息,计算包含当前基准点的最大点数。
    • 更新最大点数的值。
  5. 返回结果

    • 最终返回计算得到的最大点数。

时间复杂度分析

  • 外层循环:遍历每个点作为基准点,复杂度为 𝑂(𝑛)O(n)。

  • 内层循环:对每个基准点,计算其他点的斜率,复杂度为 𝑂(𝑛)O(n)。

  • 总时间复杂度:𝑂(𝑛2)O(n2),因为每个点都要计算与其他点的斜率信息。

  • 空间复杂度:主要由 HashMap 的空间使用决定,为 𝑂(𝑛)O(n)(在最坏情况下,每条直线都有不同的斜率)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值