携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第33天,点击查看活动详情
题目描述
给你一个数组 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 中的所有点 互不相同
解题思路
给定一个二维数组points,假定每一个二维数组中都是一组坐标,求最多在一条直线上的坐标数;
过同一个点且Δx/Δy相同的点一定在同一条直线上;
根据这个性质,可以从前向后遍历points中的所有坐标点,将相同Δx/Δy的坐标放在一个数组中,每次获取坐标数最多的那一组;
一直循环,注意,这里每次循环的元素不需要向前寻找,因为前面已经包含了后面所有元素的组合;
接下来就是Δx/Δy的计算,Δx/Δy是一个分数,所以需要对这个分数进行化简,化简又涉及到求最大公约数;
最大公约数
两个数都能够整除的最大整数,可以通过递归计算两个数的模,直到模等于0即为两个数的最大公约数;
将化简后的值,通过hash表进行存储,key为约分后的分数,value为当前斜率的所有坐标个数;
需要注意的是分母不能为0,所以当Δx或者Δy为0时需要单独处理;当Δx或者Δy为负数时,因为我们最后存的hash表的key为字符串,所以需要将负号放在统一的位置,以防在获取的时候获取不到同一个key;
所以当Δy为负,对两个值进行取反操作,这样就能保证Δy一直是正数。
代码实现
public static int maxPoints(int[][] points) {
int max = 1;
int length = points.length;
for (int i = 0; i < length-1; i++) {
int[] nowPoint = points[i];
int x = nowPoint[0];
int y = nowPoint[1];
Map<String, Integer> timesMap = new HashMap<>();
// 向后寻找元素
for (int j = i+1; j < length; j++) {
// 计算斜率
int distanceX = points[j][0] - x;
int distanceY= points[j][1] - y;
String key;
if (distanceX == 0) {
key = "x";
} else if (distanceY == 0) {
key = "y";
} else {
if (distanceY < 0) {
distanceX = -distanceX;
distanceY = -distanceY;
}
// 计算最大公约数
int maxDivisor = findMaxDivisor(distanceX, distanceY);
// 约分
distanceX /= maxDivisor;
distanceY /= maxDivisor;
key = distanceX + "/" + distanceY;
}
// 从map中取值
Integer times = timesMap.getOrDefault(key, 1);
timesMap.put(key, ++times);
max = Math.max(times, max);
}
}
return max;
}
private static int findMaxDivisor(int distanceX, int distanceY) {
int mini = Math.min(Math.abs(distanceX), Math.abs(distanceY));
int max = Math.abs(distanceX) == mini ? Math.abs(distanceY) : Math.abs(distanceX);
int temp = max % mini;
return temp == 0 ? mini : findMaxDivisor(mini,temp);
}