思路:对于点A、B、C,如何确定三点共线呢?
首先线段AB、AC的斜率是相同的,为了更好地比较,求出AB的横纵坐标之差dx,dy后,将其按最大公约数化简,然后我们,记一个key为"dx : dy",将其保存在一个map中,对应的value为2(表示该直线上已经有AB两个点了);同样地,我们对AC进行如上处理,如果key相同,则说明ABC在同一条直线上(它们有相同起点A),我们再将value+1;
为了不重复计算,我们记同线的点的最大数目为max,
我们遍历points,取位置为i的点为起点,0 <= i < len - 1; 然后从 j = i+1开始遍历,j < len, 看有多少点在以i为起点的线上,并更新max的值。
这样每次外层遍历,能找到符合的点最大数目不超过 len - i ,所以当max >= len -i ,就可以不循环了;
另外,当max的值超过len的一半时,max的值就是我们要求的,也可以跳出循环;(这个画图就很好理解)
代码如下:
<?php
class Solution {
/**
* @param Integer[][] $points
* @return Integer
*/
function maxPoints($points) {
$len = count($points);
if ($len < 2) {
return $len;
}
$maxNum = 0;
for($i=0; $i<$len-1; $i++) {
if ($maxNum > intdiv($len, 2) || $maxNum >= $len - $i) {
break;
}
// 用于记录每一趟,多少点在以i开头的线上
$ansMp = [];
for ($j=$i+1; $j<$len; $j++) {
$dx = $points[$j][0] - $points[$i][0];
$dy = $points[$j][1] - $points[$i][1];
if ($dx == 0) {
$dy = 1;
} else if ($dy == 0) {
$dx = 1;
} else {
// 保证 -2,1 与 2,-1的结果一致
if ($dy < 0) {
$dy = -$dy;
$dx = -$dx;
}
$gcd = $this->gcd($dx, $dy);
$dx = $dx / $gcd;
$dy = $dy / $gcd;
}
$key = $dx . ':' . $dy;
if (array_key_exists($key, $ansMp)) {
++$ansMp[$key];
} else {
$ansMp[$key] = 2;
}
}
foreach ($ansMp as $num) {
$maxNum = max($maxNum, $num);
}
}
return $maxNum;
}
function gcd($x, $y) {
return $y ? $this->gcd($y, $x % $y) : $x;
}
}
$s = new Solution();
var_dump($s->maxPoints([[0,0],[4,5],[7,8],[8,9],[5,6],[3,4],[1,1]]));
执行结果: