直線上最多的點數
難度困難
給定一個二維平面,平面上有 n 個點,求最多有多少個點在同一條直線上。
示例 1:
輸入: [[1,1],[2,2],[3,3]]
輸出: 3
解釋:
示例 2:
輸入: [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]]
輸出: 4
解釋:
思路
首先需要確定兩個點在一條直線上 —— 即可以透過斜率的來表示,如果一個點p1
和另外兩個點p2
,p3
都在一條直線上,那麼必有 k(p1,p2) = k(p1,p3);
所以可以透過斜率來確定與該點在同一條直線上的個數 ,如下圖
點p(0,0)
,與其餘5個點形成的斜率分別為
(0,0) - (1,5) -> k = 5
(0,0) - (1,4) -> k = 4
(0,0) - (3,3) - (4,4) -> k = 1
(0,0) - (5,1) -> k = 0.2
透過上圖可以發現共直線點數最多的是 以當前目標點 p(0,0),斜率為 1 的直線,其上加上本身共有三個點!所以我們可以對每個點維護一個記錄斜率的雜湊表!每次獲取當前斜率下的點數並判斷更新!最終獲得該點形成所有斜率的直線中,點數最多的一條!
需要注意以下幾點
- 對於重複點的處理,例如目標點(0,0) 和 待比較點 (0,0)兩個點,這時(0,0)可以與自身形成任意斜率的點,所以這個重複數量需要單獨處理
- 對於在同一豎線上的點集是無法計算他的斜率的,故應該單獨用一個標記記錄,比如 “inf”
- 如何計算斜率才能更好地保證唯一性? 可以用
dx
與dy
的最大公約數,將分子分母約分,這裡並沒有真正計算出其除法運算的結果,是因為為了避免除不盡而出現小數的不精確性!!!例如 : 3/9 和 3/7 這種,double型別的浮點數判斷相等是有誤差的,所以這我們就用斜率 的 最簡形式(不能約分)的字串來代表此唯一性的斜率,即dx / gcd(dx,dy) + "/" + dy / gcd(dx,dy)
透過程式碼體會:
package ddx.December.day8;
import java.util.HashMap;
import java.util.HashSet;
//給定一個二維平面,平面上有 n 個點,求最多有多少個點在同一條直線上。
public class Normal_149 {
public static void main(String[] args) {
int[][] points = new int[][]{
// {1, 1}, {3, 2}, {5, 3}, {4, 1}, {2, 3}, {1, 4}
//{1, 1}, {1, 2}, {1, 3}, {1, 4}, {1, 5}, {1, 6}
{0, 0}, {1, 1}, {1,-1}
};
maxPoints(points);
}
public static int maxPoints(int[][] points) {
if (points == null || points.length == 0 || points[0] == null || points[0].length == 0) {
return 0;
}
int len = points.length;
int max = 1;
for (int i = 0; i < len; i++) {
int x1 = points[i][0];
int y1 = points[i][1];
int repeat = 0;
int cur_max = 0;
HashMap<String, Integer> hashMap = new HashMap<>();//透過維護一個唯一性的斜率雜湊!
for (int j = i + 1 ; j < len; j++) {//從 i + 1開始,不需要再重複比較!
//先拿到當前基準點的橫縱座標
int x2 = points[j][0];
int y2 = points[j][1];
if (x1 == x2 && y1 == y2) {
//重複點可以為任何斜率!!!所以需要單獨記錄
repeat++;
continue;
}
if (x1 == x2) { //直線豎直!!!需要用特殊標記來記錄 - "inf"
if (!hashMap.containsKey("inf")) {
hashMap.put("inf", 1);
} else {
hashMap.put("inf", hashMap.get("inf") + 1);
}
cur_max = Math.max(hashMap.get("inf"), cur_max); //因為和當前點處於豎線的點是無法計算斜率,所以需要提前比較當前直線上的點數
continue;
}
//準備計算斜率
int dx = x2 - x1;
int dy = y2 - y1;
int _gcd = gcd(dx, dy); //計算分子分母的最大公約數
String k = (dy / _gcd) + "/" + (dx / _gcd); //使用最大公約數約分一下,保證斜率的唯一性,並用字串記錄,避免double型別的不精確
if (!hashMap.containsKey(k)) {
hashMap.put(k, 1);
} else {
hashMap.put(k, hashMap.get(k) + 1);
}
cur_max = Math.max(hashMap.get(k), cur_max); //不斷更新以point[i]為目標點的最大值
}
max = Math.max(cur_max + repeat + 1, max); //每一輪下來都得更新
System.out.println(hashMap.toString());
}
System.out.println(max);
return max;
}
public static int gcd(int x, int y) {
if (y == 0) {
return x;
} else {
return gcd(y, x % y);
}
}
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結