在给定的点集中找到所有共线的点

14
这是一个面试题:“找到给定集合中所有共线的点”。
据我理解,他们要求打印位于同一直线上的点(每两个点总是共线的)。我建议如下操作:
1.引入两种类型Line(双精度对)和Point(整数对)。
2.创建多映射: HashMap<Line,List<Point>) 3.循环遍历所有点对,并针对每个点对:计算连接点的Line,并将该行与这些点添加到多映射中。
最后,多映射包含行作为键,每行的共线点列表作为其值。
复杂度为O(N^2)。有意义吗?是否有更好的解决方案?

1
我有一种感觉,这不是完整的问题描述。有一部分丢失了。 - Nikita Rybak
2
@Michael 正如你所说,n^2的解决方案是微不足道的,而且没有更好的方法:结果大小与n^2成比例。 - Nikita Rybak
2
@Michael 是的,但这个问题不需要任何思考或算法。这就像要求对NxN矩阵中的值的平方进行求和。你怎么解决它?你遍历所有的值,并将每个平方加入到结果中。 - Nikita Rybak
1
使用一对双精度浮点数进行哈希处理效果不佳:同一行代码,但其中一个双精度浮点数的第12位小数有所差异,将会在其他位置进行哈希处理。 - Evgeni Sergeev
我在**的面试中也被问到了这个问题。 - aerin
显示剩余9条评论
3个回答

7

在没有固定两个点的情况下,"Collinear"这个词并没有什么意义。因此,在我看来,“找到给定集合中所有共线的点”并不是很有意义,除非你固定了两个点并测试其他点是否共线。

也许一个更好的问题是,在给定的集合中最多有多少共线点?在这种情况下,你可以固定两个点(只需使用2个循环),然后遍历其他点,并检查固定点和其他点之间的斜率是否相同。你可以使用以下内容进行检查,假设坐标为整数(否则可以将参数类型更改为double)。

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Returns whether 3 points are collinear
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
bool collinear(int x1, int y1, int x2, int y2, int x3, int y3) {
  return (y1 - y2) * (x1 - x3) == (y1 - y3) * (x1 - x2);
}

所以逻辑变成这样:

int best = 2;
for (int i = 0; i < number of points; ++i) {
  for (int j = i+1, j < number of points; j++) {
    int count = 2;
    for (int k = 0; i < number of points; ++k) {
      if (k==i || k==j)
        continue;

      check that points i, j and k are collinear (use function above), if so, increment count.
    }
    best = max(best,count); 
  }
}

嗯,对于这个问题,你可以用O(n^2*logn)的方法做得更好,但我不确定是否应该回答修改后的问题 :) 无论如何+1。 - Nikita Rybak
让我们问问作者是否同意更改主题 :) 或者我可以给你发电子邮件,这个想法非常简单。 - Nikita Rybak
3
这个算法的时间复杂度是否为O(n^3)? - Divick
2
计算i和j之间的斜率为什么重要,即使它以后永远不会被使用? - Maggie
@Maggie - 谢谢,你说得对,那行代码不需要。我已经把它删除了。对于我的错误表示抱歉。 - dcp
显示剩余2条评论

5

在双重平面中解决问题,将所有点转换为线段。然后运行平面扫描以报告所有交点。双重平面中的线将通过相同点表示共线点。平面扫描可以在O(nlogn)时间内完成。


1
可能需要补充的是,构建对偶线安排需要O(n^2)的时间。 - user695652
构建双线需要O(n)的时间,因为每个点都有一条线,而线的参数是点的参数的函数。然而,在最坏情况下n条线之间有多少个交点?考虑一个网格,(n/2)*(n/2),所以它是BigTheta(n^2),因此平面扫描不能在O(n lg n)的时间内完成。 - Evgeni Sergeev
@Evgeni Sergeev 构建线排列需要O(n^2)的时间,因为正如你所指出的,组合复杂度已经达到了\Omega(n^2)。而且由于原始问题是3SUM难题,因此不太可能存在一个次二的算法。 - user695652
@user695652 是的,"arrangement" 这个词是引起困惑的关键。 - Evgeni Sergeev
清楚地说,通过同一点的三条或以上直线将代表共线点? - micycle

0

你确定对运行时的分析正确吗?你说要循环遍历所有点对,其数量为n*(n-1)/2,即O(n^2)。然后你将该线和点对添加到映射中。但是,我不认为添加这样一条线 + 点组合的时间是恒定的。这意味着总体上你的时间是O(n^2 log n),而不是常数乘以n^2,这就是O(n^2)的意思。

所以真正的问题是,既然可以在O(n^2 log n)的时间内完成,那么能否在O(n^2)的时间内完成。显然,O(n^2)给出了下限,因为你必须至少打印出每个点对,其中有O(n^2)个。我的感觉是,这是一个完全普通的排序问题,人们无法期望比O(n^2 log n)更好的结果。然而,证明这个事实可能不容易。

另一个需要注意的问题是,该集合可能包含零个或一个点,因此您需要检查您的算法是否正确处理这些情况,否则编写专门处理它们的特殊情况。


根据这篇博士论文的第二章,$O(n^2)$ 的下界似乎是紧的。这篇论文中给出了一个 $O(n^2)$ 的算法:H. Edelsbrunner, J. O'Rourke, and R. Seidel. Constructing arrangements of lines and hyperplanes with applications. SIAM J. Comput., 15:341。 - Meng Lu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接