一、题目
给你一个数组 points
,其中 points[i] = [x, y]
表示 X-Y 平面上的一个点。求最多有多少个点在同一条直线上。
点击查看原题
难度级别: 困难
二、思路
1)哈希表+斜率表达
首先,考虑在同一条直线上的点,其两两之间斜率一定相等,普通的斜率表达如下所示:
k
=
y
1
−
y
0
x
1
−
x
0
k=\frac{y_1-y_0}{x_1-x_0}
k=x1−x0y1−y0 在代码中令:
{
d
i
f
f
X
=
x
1
−
x
0
d
i
f
f
Y
=
y
1
−
y
2
\left\{ \begin{aligned} diffX=x_1-x_0 \\ diffY=y_1-y_2 \end{aligned} \right.
{diffX=x1−x0diffY=y1−y2 因为比值相等时,其最简形式一定相等,求出diffX
和diffY
的最大公约数(这里使用辗转相除法),并除以最大公约数。斜率有负值的情况,但不知道是分子还是分母,假定diffX
不能小于0
,小于的话就将分子分母取相反数。
因为-10 <= x, y <= 10
,斜率采用另一种表达方式,kVal = diffY + diffX*100000
,将diffX
移到高位进行表示。
使用两层for
循环,选择一个点对其他所有点进行斜率计算,每次选择一个点都建立新的哈希表,该哈希表记录经过选取点所有直线斜率和直线上点的数量。
三、代码
1)哈希表+斜率表达
class Solution {
public int maxPoints(int[][] points) {
int ans = 1;
for (int k = 0; k < points.length; k++) {
Map<Integer, Integer> map = new HashMap();
for (int i = 0; i < points.length; i++) {
if (i == k) { // 跳过自身点
continue;
}
int diffX = points[i][0] - points[k][0]; // 获取坐标差值
int diffY = points[i][1] - points[k][1];
int gcdVal = gcd(Math.abs(diffX), Math.abs(diffY)); // 获取最大公约数
diffX = diffX / gcdVal;
diffY = diffY / gcdVal;
if (diffX < 0) { // 指定diffX不能为小于0的数
diffX = -diffX;
diffY = -diffY;
}
int kVal = diffY + diffX*100000; // 通过将diffX移动到高位形成唯一的斜率编码
map.put(kVal, map.getOrDefault(kVal, 1) + 1);
}
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
ans = Math.max(ans, entry.getValue());
}
}
return ans;
}
private int gcd(int a, int b) { // 辗转相除法
return b != 0 ? gcd(b, a%b) : a;
}
}
n
为点总数,m
为横纵坐标差最大值,时间复杂度O(n^2*logm)
,空间复杂度为O(n)