问题陈述
给定具有x和y坐标的2D平面中的n个点。如果可以通过乘以相同的数字从另一个获得一个点,则两个点是相同的。例如:(10,15)和(2,3)是相同的,而(10,15)和(10,20)不是。建议一个O(n)算法,确定输入的n个点是否包含两个相同的点。
简单的方法可以只是检查每个点,即如果有5个点,第一个我有4个比较,第二个我有3个比较,依此类推。但这不是O(n)时间复杂度解决方案。我真的无法提前思考。有什么建议吗?
答案 0 :(得分:4)
一个明显(但可能不充分)的可能性是将每个点减少到表示比率的浮点数,因此(2,3)和(10,15)都变为0.66667
,和(10, 20)成为0.5
。
这个不会工作的原因是浮点数往往是近似的,所以你只需要使用近似的比较,并忍受它会显示的事实只要它们等于(比方说)15个小数位,它们就是相同的。
如果您不想这样做,您可以创建一个支持比较的有理数字类(例如,将每个比率降低到最低项)。
无论哪种方式,一旦你将一个点减少到一个数字,你只需将每个点插入(对于一种可能性)哈希表。当您插入每个时,检查该比率是否已经在哈希表中 - 如果是,则您具有相同的点。如果没有,请正常插入。
答案 1 :(得分:2)
将一个点减少到一个数字的一种方法是将该点的第一个坐标乘以其他点的所有第二个坐标的乘积。
所以对于例如:
(10, 20) -> 10 * 10 * 4 = 400
(5, 10) -> 5 * 20 * 4 = 400
(3, 4) -> 3 * 20 * 10 = 600
第一和第二点匹配。对于大的点集,产品将非常大,并且需要使用BigNumber(将大于O(n))但是您可以通过在每次乘法后取模来将数字保持在合理的限制内。然后按照Jerry Coffin的回答中的建议使用哈希表。
您可以通过单个正向传递轻松计算所有第二个坐标的乘积,然后单个向后传递阵列并保持运行的产品:
e.g。在Java中:
long m = 9223372036854775783L; // largest prime less than max long
int[][] points = {{1, 2}, {1, 3}, {1, 4}, {1, 5}, {2, 6}};
long[] mods = new long[points.length];
long prod = 1;
for(int i = 0; i < points.length; i++)
{
mods[i] = prod;
prod = (points[i][1] * prod) % m;
}
prod = 1;
for(int i = points.length - 1; i >= 0 ; i--)
{
mods[i] = (mods[i] * prod) % m;
prod = (points[i][1] * prod) % m;
}
HashSet<Long> set = new HashSet<Long>();
for(int i = 0; i < points.length; i++)
{
prod = (mods[i] * points[i][0]) % m;
if(set.contains(prod))
System.out.println("Found a match");
set.add(prod);
}
该算法假设所有坐标都是整数!= 0.零可以作为特殊情况处理:所有在第一位置为零的点彼此匹配,同样对于那些在第二位置为零的点,和(0 ,0)匹配所有点。作为优化,通过数组的第二次和第三次传递可以合并为单次传递。