算法練習每日一題:直線上最多的點數

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

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/max-points-on-a-line

class Solution:
    def maxPoints(self, points):
        """
        :type points: List[Point]
        :rtype: int
        """   
        # 如果 points 中元素少於3,也就是兩個點,那它們一定在一條直線上
        self.points = points
        n = len(self.points)
        if n < 3:
            return n
        
        max_count = 1
        # 遍歷 hash 超出最多的點的線條,找出 count 的最大值 
        for i in range(n - 1):
            max_count = max(self.max_points_on_a_line_containing_point_i(i, n), max_count)
        return max_count
        
    def add_line(self, i, j, count, duplicates):
        """
        畫線,判斷點是否在一條線上
        普通線條(斜率),記錄所在點數
        重複或 180 度線條,記錄起點數
        """
        # 轉換爲座標
        x1 = self.points[i][0]
        y1 = self.points[i][1]
        x2 = self.points[j][0]
        y2 = self.points[j][1]
        # 處理重複的點,平行的線條;注意平行線條斜率無窮大,不存在斜率,需要單獨處理
        # 一個點可能在多個線條上,則選擇點數最多的那個線條
        if x1 == x2 and y1 == y2:  
            duplicates += 1
        elif y1 == y2:
            self.horisontal_lines += 1
            count = max(self.horisontal_lines, count)
        else:
            slope = (x1 - x2) / (y1 - y2)
            self.lines[slope] = self.lines.get(slope, 1) + 1
            count = max(self.lines[slope], count)
        return count, duplicates
        
    def max_points_on_a_line_containing_point_i(self, i, n):
        """
        計算每條線的最多點數
        """
        # 初始化 dict,只要有 2 個以上的點,則一定有一條線
        self.lines, self.horisontal_lines = {}, 1
        # 編碼是從 i + 1 開始的,count 計數默認值應該是 1
        count = 1
        # 與其相同(重複的點),在垂直或平行方向上的點數,默認爲 0 
        duplicates = 0
        for j in range(i + 1, n):
            count, duplicates = self.add_line(i, j, count, duplicates)
        return count + duplicates         

題解

  • 我最開始分析這道題,先了解在二維座標內,成爲直線的情況,充分必要條件;因爲題目的數據是整數,也比較好分析,常見的情況就是 45 度,90 度,180 度的線條,在這些線條上假如存在連續的點,那麼他們的座標值 (x, y) 也即 [1, 1]應該是一個等差數列。如果點是不連續的,或有其他情況,等差有不同的值;類似的有考慮到使用斜率的方法,官方給出的枚舉方法,可以解決點不連續的問題,從一個點出發,看是否有其他點在同一條直線上,應用斜率的推導公式。

  • 另外一個前提,平面內任意 2 點都可以畫一條直線,問題是在第 3 點起,及之後的點的判斷;就是一個 2 重循環的判斷,對於列表中任一點 i (xi, yi), 考慮其他點 j (xj, yj) ,i + 2 < j < n-1,編碼中是從 i + 1 起,因爲要先算出一個斜率來;

  • 使用 hash 表,也即 dict 保存鍵 i, 值是 count, 在一條線上的點的數量,在循環判斷中如果新的點計算斜率相同,count + 1;

  • 這裏面有個問題,可能存在重複的線條,所以爲了區分每一條線條,應該將斜率作爲 hash 表的鍵,而且其能保證是不重複的線條;要注意的是水平線條不存在斜率,需要單獨處理。

請同時參考官方題解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章