1. 程式人生 > >Leetcode 149:直線上最多的點數(最詳細的解法!!!)

Leetcode 149:直線上最多的點數(最詳細的解法!!!)

給定一個二維平面,平面上有 n 個點,求最多有多少個點在同一條直線上。

示例 1:

輸入: [[1,1],[2,2],[3,3]]
輸出: 3
解釋:
^
|
|        o
|     o
|  o  
+------------->
0  1  2  3  4

示例 2:

輸入: [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]]
輸出: 4
解釋:
^
|
|  o
|     o        o
|        o
|  o        o
+------------------->
0  1  2  3  4  5  6

解題思路

這個問題我們有一個最簡單的思路,就是我們先從所有點中找出兩個點,然後用這兩個點構成一條直線,然後再計算有多少點在這個直線上,我們記錄它。接著找下一條直線,直到我們計算完所有直線對應的點的個數。這種解法的時間複雜度是O(n^3),顯然太慢了,而且實際操作起來也很麻煩。

我們知道一個線段是由斜率和截距決定的,實際上如果我們知道了一個直線上的點和這條直線的斜率也能確定這條直線。基於此,我們可以遍歷所以點point,計算其餘點和point構成的直線的斜率,如果斜率相同,那麼說明兩個點在一條直線上,我們只要記錄點的個數,最後找到最大值即可。對於上述演算法我要注意一個問題,就是存在相同點的情況,我們需要對此加以處理。

from collections import defaultdict
class Solution:
    def maxPoints(self, points):
        """
        :type points: List[Point]
        :rtype: int
        """
        slopes, result = defaultdict(int), 0
        for i, point1 in enumerate(points): 
            slopes.clear()
            duplicate =
1 for _, point2 in enumerate(points[i+1:]): if point1.x == point2.x and point1.y == point2.y: duplicate += 1 continue slope = float('inf') if point1.x == point2.x else \ (point1.y - point2.y)/(point1.x - point2.x) slopes[slope] += 1 if result < duplicate: result = duplicate for _, val in slopes.items(): if val + duplicate > result: result = val + duplicate return result

但是實際操作的過程中,我們發現上面的程式碼還有一個bug。例如

[Point(0, 0), Point(94911151, 94911150), Point(94911152, 94911151)]
94911150/94911151=0.9999999894638303
94911151/94911152=0.9999999894638303

我們發現,對於斜率很靠近的線,python的計算精度不夠。我們可以使用Decimal

from decimal import Decimal
Decimal((point2.y - point1.y))/Decimal((point2.x - point1.x))

但是為此我們需要犧牲一些計算上的耗時。另外一種策略是我們不計算斜率的具體數值,而是通過分式表示。這就涉及到了如何計算最簡分式的問題,我們可以通過計算最大公約數來解決,具體操作可以看我的CPP實現。

現在我們將演算法優化到了O(n^2)這個級別。有沒有更好的呢?暫時沒有找到更好的辦法,留待以後解決。

我將該問題的其他語言版本新增到了我的GitHub Leetcode

如有問題,希望大家指出!!!