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 表的键,而且其能保证是不重复的线条;要注意的是水平线条不存在斜率,需要单独处理。
请同时参考官方题解。