给你一个数组 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;
}
}
解题思路
-
基本情况处理:
- 如果点的总数小于等于2,直接返回点的数量,因为任何两点或单点都可以构成一条直线。
-
计算直线斜率:
- 对于每个点作为基准点,计算它与其他所有点的斜率。斜率的计算是通过两点之间的x和y坐标差值来实现的。
- 处理斜率的特殊情况:
- 如果x为0,则斜率为垂直线,可以用
y = 1
来表示。 - 如果y为0,则斜率为水平线,可以用
x = 1
来表示。
- 如果x为0,则斜率为垂直线,可以用
- 对于一般情况,计算斜率的最简形式,通过最大公约数化简斜率。
-
存储斜率信息:
- 使用
HashMap
存储每个斜率对应的点数。 - 将斜率表示为一个唯一的键值对
y + x * 20001
,其中 20001 是为了避免斜率表示中的负值冲突。
- 使用
-
计算最大点数:
- 遍历所有斜率信息,计算包含当前基准点的最大点数。
- 更新最大点数的值。
-
返回结果:
- 最终返回计算得到的最大点数。
时间复杂度分析
-
外层循环:遍历每个点作为基准点,复杂度为 𝑂(𝑛)O(n)。
-
内层循环:对每个基准点,计算其他点的斜率,复杂度为 𝑂(𝑛)O(n)。
-
总时间复杂度:𝑂(𝑛2)O(n2),因为每个点都要计算与其他点的斜率信息。
-
空间复杂度:主要由
HashMap
的空间使用决定,为 𝑂(𝑛)O(n)(在最坏情况下,每条直线都有不同的斜率)。