Article / 文章
LeetCode 149 直线上最多的点数:哈希表优化斜率计算
给定一个数组 points ,其中 points[i] = [xi, yi] 表示 X-Y 平面上的一个点,求最多有多少个点在同一条直线上。
题目描述
给定一个数组 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 <= 300points[i].length == 2-10^4 <= xi, yi <= 10^4points中的所有点 互不相同
解题思路
方法一:枚举直线 + 哈希表
对于每个点,我们可以枚举其他所有点,计算它们之间的斜率,使用哈希表统计相同斜率的点的数量。
关键点:
- 使用最大公约数化简斜率,避免浮点数精度问题
- 处理垂直线(斜率为无穷大)的特殊情况
- 处理重复点的情况
具体步骤:
- 遍历每个点作为基准点
- 对于每个基准点,遍历其他所有点:
- 计算两点之间的斜率
- 使用哈希表统计相同斜率的点的数量
- 更新最大点数
时间复杂度:O(n²),其中n是点的数量。 空间复杂度:O(n),哈希表存储斜率。
方法二:枚举直线 + 排序
对于每个点,我们可以计算其他所有点相对于该点的极角,然后排序统计相同极角的点的数量。
关键点:
- 使用atan2计算极角
- 处理重复点的情况
- 处理精度问题
时间复杂度:O(n² log n),其中n是点的数量。 空间复杂度:O(n),存储极角数组。
图解思路
以示例2为例,演示枚举过程:
- 以点[1,1]为基准点:
斜率统计:
- 1/2: [3,2]
- 2/3: [5,3]
- 0: [4,1]
- 2: [2,3]
- 3: [1,4]
最大点数:2
- 以点[3,2]为基准点:
斜率统计:
- 1/2: [5,3]
- -1: [4,1]
- -1/2: [2,3]
- -1/2: [1,4]
最大点数:3
代码实现
C# 实现
public class Solution {
public int MaxPoints(int[][] points) {
int n = points.Length;
if (n <= 2) return n;
int maxPoints = 0;
for (int i = 0; i < n; i++) {
Dictionary<string, int> slopeCount = new Dictionary<string, int>();
int samePoints = 1;
for (int j = i + 1; j < n; j++) {
if (points[i][0] == points[j][0] && points[i][1] == points[j][1]) {
samePoints++;
continue;
}
string slope = GetSlope(points[i], points[j]);
slopeCount[slope] = slopeCount.GetValueOrDefault(slope, 0) + 1;
}
int maxSlopeCount = slopeCount.Values.Count > 0 ? slopeCount.Values.Max() : 0;
maxPoints = Math.Max(maxPoints, maxSlopeCount + samePoints);
}
return maxPoints;
}
private string GetSlope(int[] p1, int[] p2) {
int dx = p2[0] - p1[0];
int dy = p2[1] - p1[1];
if (dx == 0) return "vertical";
if (dy == 0) return "horizontal";
int gcd = GCD(Math.Abs(dx), Math.Abs(dy));
dx /= gcd;
dy /= gcd;
return $"{dy}/{dx}";
}
private int GCD(int a, int b) {
return b == 0 ? a : GCD(b, a % b);
}
}
Python 实现
class Solution:
def maxPoints(self, points: List[List[int]]) -> int:
n = len(points)
if n <= 2:
return n
max_points = 0
for i in range(n):
slope_count = {}
same_points = 1
for j in range(i + 1, n):
if points[i] == points[j]:
same_points += 1
continue
slope = self.get_slope(points[i], points[j])
slope_count[slope] = slope_count.get(slope, 0) + 1
max_slope_count = max(slope_count.values()) if slope_count else 0
max_points = max(max_points, max_slope_count + same_points)
return max_points
def get_slope(self, p1, p2):
dx = p2[0] - p1[0]
dy = p2[1] - p1[1]
if dx == 0:
return "vertical"
if dy == 0:
return "horizontal"
gcd = self.gcd(abs(dx), abs(dy))
dx //= gcd
dy //= gcd
return f"{dy}/{dx}"
def gcd(self, a, b):
return a if b == 0 else self.gcd(b, a % b)
C++ 实现
class Solution {
public:
int maxPoints(vector<vector<int>>& points) {
int n = points.size();
if (n <= 2) return n;
int maxPoints = 0;
for (int i = 0; i < n; i++) {
unordered_map<string, int> slopeCount;
int samePoints = 1;
for (int j = i + 1; j < n; j++) {
if (points[i][0] == points[j][0] && points[i][1] == points[j][1]) {
samePoints++;
continue;
}
string slope = getSlope(points[i], points[j]);
slopeCount[slope]++;
}
int maxSlopeCount = 0;
for (const auto& pair : slopeCount) {
maxSlopeCount = max(maxSlopeCount, pair.second);
}
maxPoints = max(maxPoints, maxSlopeCount + samePoints);
}
return maxPoints;
}
private:
string getSlope(const vector<int>& p1, const vector<int>& p2) {
int dx = p2[0] - p1[0];
int dy = p2[1] - p1[1];
if (dx == 0) return "vertical";
if (dy == 0) return "horizontal";
int gcd = GCD(abs(dx), abs(dy));
dx /= gcd;
dy /= gcd;
return to_string(dy) + "/" + to_string(dx);
}
int GCD(int a, int b) {
return b == 0 ? a : GCD(b, a % b);
}
};
性能分析
各语言实现的性能对比:
| 实现语言 | 执行用时 | 内存消耗 | 特点 |
|---|---|---|---|
| C# | 92 ms | 38.2 MB | 实现简洁,性能适中 |
| Python | 156 ms | 16.8 MB | 代码最简洁 |
| C++ | 24 ms | 9.6 MB | 性能最优 |
补充说明
代码亮点
- 使用最大公约数化简斜率,避免浮点数精度问题
- 处理了重复点和特殊直线的情况
- 使用哈希表优化查找效率
常见错误
- 没有处理重复点的情况
- 使用浮点数表示斜率导致精度问题
- 没有处理垂直线和水平线的特殊情况
相关题目
讨论
有几个问题可以思考一下:
- 为什么要用最大公约数来简化斜率?直接用浮点数计算斜率不行吗?
- 这道题标注为“困难”,你觉得难在哪里?
- 除了斜率法,还有其他方法判断点是否在同一直线上吗?
欢迎在评论区讨论。
如果你都看到这里了,说明还是有点收获的吧?给个赞鼓励一下呗。