给你一个数组 points
,其中 points[i] = [xi, yi]
表示 X-Y 平面上的一个点。求最多有多少个点在同一条直线上。
示例 :
输入:points = [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]] 输出:4
一个点加一个斜率即可唯一的确定一条直线。
所以我们可以对「点」进行分类然后去求解,问题转换成,经过某个点的直线,哪条直线上的点最多。
纯思路代码,无优化:
class Solution {
public int maxPoints(int[][] points) {
int size = points.length;
if(size <= 2){
return size;
}
boolean[][] flag = new boolean[size][size];
double[][][] kk = new double[size][size][size];
int result = 0;
for(int k = 0;k < size;k++){
for(int i = 0;i < size;i++){
if(points[k][0] != points[i][0]){
kk[k][i][0] = (points[k][1] - points[i][1]);
kk[k][i][1] = (points[k][0] - points[i][0]);
}else{
flag[k][i] = true;
}
}
for(int i = 0;i < size-1;i++){
int temp = 0;
if(flag[k][i] != true){
for(int j = 0;j < size;j++){
if((points[j][0] - points[k][0])*kk[k][i][0]/kk[k][i][1] == points[j][1] - points[k][1]){
temp++;
}
}
}else{
for(int j = 0;j < size;j++){
if(points[j][0] == points[k][0]){
temp++;
}
}
}
if(temp > result){
result = temp;
}
}
}
return result;
}
}
首先,检查给定的点集的大小。如果大小小于等于2,则直接返回该大小,因为至少需要两个点才能构成一条直线。
然后,创建布尔类型的二维数组flag和一个三维浮点数数组kk。flag用于标记是否存在斜率为无穷大的直线,而kk用于存储每对点之间的斜率。
接下来,使用两个嵌套循环来遍历所有点的组合。对于每一对点,它计算它们的斜率并将其存储在kk数组中。如果两点的x坐标相同,则将flag数组对应位置设置为true,表示存在斜率为无穷大的直线。
然后,使用另一个嵌套循环来计算每个点与其他点构成的直线上的点的数量。如果flag数组对应位置为false,则使用斜率公式计算其他点的y坐标,并与当前点的y坐标进行比较。如果相等,则说明该点与当前点在同一条直线上,将计数器temp加1。如果flag数组对应位置为true,则直接检查其他点的x坐标是否与当前点的x坐标相等,如果相等,则将计数器temp加1。
最后,更新结果变量result,使其保持最大的计数值。
注意,这里斜率可能是无穷小数,所以要注意存储斜率对应的分子、分母以及判断等式的构造。
使用hashmap优化:
class Solution {
public int maxPoints(int[][] points) {
int n = points.length;
if (n <= 2)return n;
int result = 0;
for (int i = 0; i < n; i++) {
int temp = 0;
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
if (result >= n - i || result > n / 2) {
break;
}
for (int j = i + 1; j < n; j++) {
int x = points[i][0] - points[j][0];
int y = points[i][1] - points[j][1];
if (x == 0) {
y = 1;
} else if (y == 0) {
x = 1;
} else {
if (y < 0) {
x = -x;
y = -y;
}
int gcd = gcd(Math.abs(x), Math.abs(y));
x = x / gcd;
y = y / gcd;
}
int key = y + x * 17;
map.put(key, map.getOrDefault(key, 0) + 1);
}
for (Map.Entry<Integer, Integer> entry: map.entrySet()) {
int num = entry.getValue();
temp = Math.max(temp, num + 1);
}
result = Math.max(result, temp);
}
return result;
}
public int gcd(int a, int b) {
if(a == 1 || b == 1)return 1;
while(b > 0){
int tmp = a;
a = b;
b = tmp % b;
}
return a;
}
}
在内层循环中,计算当前点与后续点之间的x和y坐标差值。如果x或y为0,则将其设置为1,以避免除以0的情况。否则,根据x和y的正负关系,将它们转换为正数,并计算它们的最大公约数(gcd)。
然后,根据x和y的值计算出一个唯一的键值key
,并将其存储在一个哈希表map
中。键值的计算方式是将y加上17乘以x,这样可以保证不同的斜率对应不同的键值。
在外层循环结束后,遍历哈希表map
中的每个条目,找到出现次数最多的斜率对应的点数,并将其加1后与temp
比较取较大值。最后,将temp
与result
比较取较大值,更新result
的值。