题目描述
给定一个二维平面,平面上有 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
思路:
解法一:
用y = kx + b 来表示直线,遍历每一条直线,针对每一条直线然后遍历统计直线上的点。
很冗长的C++.... 而且写的时候把k和b这两个数用分子和分母来表示显得很不友好。
//分数
struct FenShu{
int a=1;
int b=1;
FenShu(int a, int b) : a(a), b(b) {}
};
FenShu k={0,1};
FenShu b ={0,1};
//二维平面直线函数 y = kx + b
bool flag = false;
int x = 0;
void get_k_b(Point & point1, Point & point2){
if((double)(point1.x - point2.x) != 0){
k.b = point1.y - point2.y;
k.a = point1.x - point2.x;
b.a = k.a;
b.b = point1.y * k.a - point1.x * k.b;
flag = false;
} else{
x = point1.x;
flag = true;
}
}
//是否在直线上
bool onLine(Point & point){
if(flag){
return point.x == x;
}
//用long long 防止Int 越界
long long left = (long long)point.y * (long long)k.a;
long long right = (long long)point.x * k.b + (long long)b.b;
return left == right;
}
int max_76(int x, int y){
return x > y ? x : y;
}
/**
* 给定一个二维平面,平面上有 n 个点,求最多有多少个点在同一条直线上
* @param points
* @return
*/
int maxPoints(vector<Point>& points) {
if(points.size() <=2 ){
return points.size();
}
int max = 0;
for(int i = 0; i < points.size()-1; i++){
for(int j = i+1; j < points.size(); j++){
get_k_b(points[i],points[j]); //两点确定一条直线
int subMax = 0; //有多少个点在该直线上
for(int a = 0; a < points.size(); a++){
if(onLine(points[a])){
subMax++;
}
}
max = max_76(max, subMax);
if(subMax == points.size()){
return max;
}
}
}
return max;
}
解法二:
这种解法就高级了很多! 首先让我们明确一个道理:两个不相同的点确定一条直线,我们应该遍历所有的直线。
如果有n个点的话,那么共有n(n-1)/2 条直线,那么程序里面应该怎么写呢?难道是
for(int i = 0; i < n; i++)
{
for(int j =0; j < n; j++)
{
if( j != i) {........}
}
}
这样的写法显然会有重复。所以要改进,实际上,上述问题也是n个点求两两组合的问题。举个简单的例子: a b c d e 五个点,我们可以这样遍历:ab ac ad ae bc bd be cd ce de . 所以正确的框架为:
for(int i=0; i<n-1; i++)
{
for(int j=i+1; j<n; j++) {}
}
在本题中因为是取两点,然后确定之后的点是否在这两点构成的直线上,所以区间各减一,也就是
for(int i=0; i<n-2; i++)
{
for(int j=i+1; j<n-1)
{
for(int k=j+1; k<n; k++) {验证k是否在有i j构成的直线上}
}
}
接下来,再考虑一下这道题的特殊情况,就是第i点的后面有可能有与i点相同的点(重复点),所以这时候要把i更新为最后的那个i点,同样的对j遍历的时候也是一样的。
So, 力推第二种解法!!!
int maxPoints(vector<Point>& points) {
sort(points.begin(),points.end(),cmp); //按照cmp规则对数组排序
if(points.size()<3)
return points.size(); //0 1 2的情况
int ans=2,count1=1,count2=1;
for(int i=0;i<points.size()-2;i++){ //i的区间是从第一个点到倒数第三个点
count1=1;
while(i<points.size()-3 && points[i+1].x==points[i].x && points[i+1].y==points[i].y){
count1++; //重复点处理,count1就是当前的i重复点的个数
i++;
}
for(int j=i+1;j<points.size()-1;j++){
count2=1;
while(j<points.size()-2 && points[j+1].x==points[j].x && points[j+1].y==points[j].y){
count2++; //重复点处理,当前j的重复点的个数
j++;
}
int tmp=count1+count2; //由i j构成的这条直线上已经有了这么多点
for(int k=j+1;k<points.size();k++){
if(isLine(points[i],points[j],points[k]))
tmp++;
}
ans=ans>tmp?ans:tmp; // ans = max(ans, tmp);
}
}
return ans;
}
static bool cmp(Point& a,Point& b){ //注意这里:sort的比较函数必须写在类外部(即全局区域)或者声明为静态函数
if(a.x!=b.x)
return a.x<b.x; //按照点的横坐标进行排序
else
return a.y<b.y;
}
bool isLine(Point& a,Point& b,Point& c){
long long tmp1=((long long)a.x-b.x)*((long long)a.y-c.y); //可能超出int的范围 所有用 long long
long long tmp2=((long long)a.x-c.x)*((long long)a.y-b.y);
// 这里注意三个点P1(x1,y1) P2(x2, y2) P3(x3, y3) 共线满足 (y1-y2)/(x1-x2) == (y1-y3)/(x1-x3) 分母可能为0 故直接改为
// 乘法: (y1-y3)*(x1-x2)==(y1-y2)*(x1-x3) 就是共线的充要条件
return tmp1 == tmp2 ? true : false;
}
解法三:
现学现卖 Java版
class Solution {
public int maxPoints(Point[] points) {
if(points.length == 0) return 0;
if(points.length == 1) return 1;
if(points.length == 2) return 2;
int ans = 2;
int Max = -1;
for(int i=0;i<points.length;i++){
for(int j=i+1;j<points.length;j++){
ans = 2;
for(int k=0;k<points.length;k++){
if(k == i || k == j) continue;
int x1 = points[i].x;
int y1 = points[i].y;
int x2 = points[j].x;
int y2 = points[j].y;
int x3 = points[k].x;
int y3 = points[k].y;
if(x1 == x2){
if(y1 == y2) {
if( x2 == x3 && y2 == y3){
ans++;
}else{
continue;
}
}else {
if(x2 == x3)
ans++;
}
}
else{
long tanK1 = ((long)x3-x1)*((long)y2-y1);
long tanK2 = ((long)y3-y1)*((long)x2-x1);
if(tanK1 == tanK2) ans++;
}
}
if(Max < ans) Max = ans;
}
}
return Max;
}
}