LeetCode 149. 直线上最多的点数(C语言详解 | 斜率 + 最大共线点)

一、题目描述

给定二维平面上的一些点 points,其中:

复制代码
points[i] = [xi, yi]

表示平面上的一个点 (xi, yi)

请你求出 最多有多少个点在同一条直线上

示例 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
-10^4 <= xi, yi <= 10^4
points 中所有点互不相同

二、题目思路分析

如果多个点在同一条直线上,那么 它们之间的斜率一定相同

假设固定一个点 (x1,y1),再选择另一个点 (x2,y2)

复制代码
slope = (y2 - y1) / (x2 - x1)

如果第三个点 (x3,y3) 满足:

复制代码
(y2 - y1)/(x2 - x1) = (y3 - y1)/(x3 - x1)

则说明三点共线。

因此可以得到核心思路:

复制代码
固定一个点
计算它与其他点的斜率
统计相同斜率的数量
最大值 + 1(包括自己)

由于点最多 300 个,因此枚举起点并统计斜率即可。

时间复杂度:

复制代码
O(n²)

三、解法一:暴力枚举三点(不推荐)

最直接的方法是枚举三个点,判断是否共线。

判断三点共线:

复制代码
(x2-x1)*(y3-y1) == (x3-x1)*(y2-y1)

C语言实现

复制代码
int maxPoints(int** points, int pointsSize, int* pointsColSize) {

    if (pointsSize <= 2)
        return pointsSize;

    int max = 2;

    for (int i = 0; i < pointsSize; i++) {

        for (int j = i + 1; j < pointsSize; j++) {

            int count = 2;

            for (int k = j + 1; k < pointsSize; k++) {

                int x1 = points[i][0];
                int y1 = points[i][1];
                int x2 = points[j][0];
                int y2 = points[j][1];
                int x3 = points[k][0];
                int y3 = points[k][1];

                if ((x2 - x1) * (y3 - y1) == (x3 - x1) * (y2 - y1))
                    count++;
            }

            if (count > max)
                max = count;
        }
    }

    return max;
}

复杂度

复制代码
时间复杂度:O(n³)
空间复杂度:O(1)

n=300 时可能会超时,因此一般不采用。


四、解法二:斜率统计(推荐)

核心思想:

复制代码
固定一个点
统计所有斜率
斜率相同 → 在同一直线

但有一个关键问题:

复制代码
浮点数精度误差

例如:

复制代码
1/3
2/6

理论上相同,但浮点计算可能不同。

因此需要:

复制代码
使用最简分数 dy/dx

利用 最大公约数 gcd 进行约分。

例如:

复制代码
dy = 4
dx = 2
→ 2/1

五、算法步骤

  1. 枚举每一个点 i

  2. 计算 i 与其它点 j 的斜率 (dy,dx)

  3. gcd 进行约分

  4. 统计相同斜率数量

  5. 更新最大值


六、C语言实现(推荐)

复制代码
#include <stdlib.h>

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

int maxPoints(int** points, int pointsSize, int* pointsColSize) {

    if (pointsSize <= 2)
        return pointsSize;

    int result = 0;

    for (int i = 0; i < pointsSize; i++) {

        int slopes[600][2];
        int count[600] = {0};
        int size = 0;
        int localMax = 0;

        for (int j = i + 1; j < pointsSize; j++) {

            int dx = points[j][0] - points[i][0];
            int dy = points[j][1] - points[i][1];

            int g = gcd(abs(dx), abs(dy));

            dx /= g;
            dy /= g;

            int found = 0;

            for (int k = 0; k < size; k++) {

                if (slopes[k][0] == dx && slopes[k][1] == dy) {
                    count[k]++;
                    if (count[k] > localMax)
                        localMax = count[k];
                    found = 1;
                    break;
                }
            }

            if (!found) {

                slopes[size][0] = dx;
                slopes[size][1] = dy;
                count[size] = 1;

                if (count[size] > localMax)
                    localMax = count[size];

                size++;
            }
        }

        if (localMax + 1 > result)
            result = localMax + 1;
    }

    return result;
}

七、复杂度分析

复制代码
时间复杂度:O(n²)
空间复杂度:O(n)

其中:

复制代码
外层枚举点
内层统计斜率

八、优化技巧(面试加分)

1 提前结束

如果:

复制代码
当前最大值 >= 剩余点数

则可以提前退出循环。


2 处理特殊情况

垂直线:

复制代码
dx = 0

统一表示为:

复制代码
(0,1)

水平线:

复制代码
dy = 0

统一表示为:

复制代码
(1,0)

九、总结

方法 时间复杂度 推荐
暴力枚举 O(n³)
斜率统计 O(n²)

核心思想:

复制代码
固定一个点
统计所有斜率
斜率相同 → 共线

通过 gcd 约分避免浮点误差,即可得到最终答案。

相关推荐
别或许2 小时前
03线性代数之向量组(知识总结)
线性代数·算法·机器学习
仰泳的熊猫2 小时前
题目2267:蓝桥杯2016年第七届真题-取球博弈
数据结构·c++·算法·蓝桥杯·深度优先·图论
逆境不可逃2 小时前
LeetCode 热题 100 之 152. 乘积最大子数组 416. 分割等和子集 32. 最长有效括号 62. 不同路径
算法·leetcode·职场和发展
DeepModel2 小时前
【概率分布】几何分布超详细解析
算法·概率论
Genevieve_xiao2 小时前
【差分】差分的理解与基础题型总结
数据结构·c++·算法
舟舟亢亢2 小时前
算法总结——【技巧,ACM模式输入】
算法
智者知已应修善业2 小时前
【无序数组指针交换2则】2024-10-28
c语言·数据结构·c++·经验分享·笔记·算法
liulilittle2 小时前
MIMT审计技术:TLS信任链的脆弱性与资本主义商业逻辑下的必然
网络·c++·tcp/ip·tls·mimt
一叶落4382 小时前
LeetCode 136. 只出现一次的数字(C语言详解 | 哈希表 + 排序 + 位运算)
c语言·数据结构·算法·leetcode·哈希算法·散列表