【哈希表】B015_LC_面试题:最佳直线(暴力+数学 / 记录同一斜率上点数目)

一、Problem

给定一个二维平面及平面上的 N 个点列表Points,其中第i个点的坐标为Points[i]=[Xi,Yi]。请找出一条直线,其通过的点的数目最多。

设穿过最多点的直线所穿过的全部点编号从小到大排序的列表为S,你仅需返回[S[0],S[1]]作为答案,若有多条直线穿过了相同数量的点,则选择S[0]值较小的直线返回,S[0]相同则选择S[1]值较小的直线返回。

示例:

输入: [[0,0],[1,1],[1,0],[2,0]]
输出: [0,2]
解释: 所求直线穿过的3个点的编号为[0,2,3]

提示:

2 <= len(Points) <= 300
len(Points[i]) = 2

二、Solution
方法一:几何

思路

两个点 p o i n t i 、 p o i n t j point_i、point_j pointipointj 确定一条直线,用一般式 a x + b y + c = 0 ax+by+c=0 ax+by+c=0 作为判断公式统计在同一直线上的点的数量

a、b、c 怎么求?三个点可确定两条直线,又有直线的两点式
x − x 1 x 2 − x 1 = y − y 1 y 2 − y 1 \cfrac{x - x_1 }{x_2 - x_1} = \cfrac{y - y_1}{ y_2 - y_1} x2x1xx1=y2y1yy1

两点式可转为直线的一般式
a x + b x + c = 0 ax + bx + c = 0 ax+bx+c=0

通分之后,代入系数解得 a、b、c:

a = y 2 − y 1 , b = x 1 − x 2 , 移 项 可 得 − c = a × x 1 + b × y 1 a = y_2 - y_1,b = x_1 - x_2,移项可得 -c = a × x_1 + b × y_1 a=y2y1b=x1x2c=a×x1+b×y1

class Solution {
public:
	bool isSameLine(vector<int>& p1, vector<int>& p2, vector<int>& p) {
		int x=p[0], x1=p1[0], x2=p2[0], y=p[1], y1=p1[1], y2=p2[1];
		long a=y2-y1, b=x1-x2, c=a*x1+b*y1;
		return a*x+b*y-c==0;
	}
    vector<int> bestLine(vector<vector<int>>& ps) {
    	int n=ps.size(), mx=-1;
    	vector<int> ans(2);

    	for (int i=0; i<n-1; i++)
		for (int j=i+1; j<n; j++) {
			int same=0;
			for (auto& p : ps) if (isSameLine(ps[i], ps[j], p))
				same++;
			if (same > mx) {
				ans[0]=i, ans[1]=j, mx=same;
			} else if (same == mx && i==ans[0] && j<ans[1]) {
				ans[1]=j;
			}
		}
		return ans;
    }
};

1628 ms…

复杂度分析

  • Time O ( n 3 ) O(n^3) O(n3)
  • Space O ( 1 ) O(1) O(1)

可优化的地方是,利用斜率公式统计同一直线上点数量,可优化掉第三个循环,大致的思路是这样,处理一下重叠的点,写了有点久…

class Solution {
public:
	int gcd(int a, int b) {
		return a==0 ? b : gcd(b%a, a);
	}
    vector<int> bestLine(vector<vector<int>>& ps) {
    	int n=ps.size(), mx=-1;
    	vector<int> ans(2);
    	for (int i=0; i<n-1; i++) {
			unordered_map<string, vector<int>> cnt;
            int c=0, overlap=0;
			for (int j=i+1; j<n; j++) {
				int dx=ps[j][0]-ps[i][0], dy=ps[j][1]-ps[i][1], gcd_xy=(dx, dy);
                if (dx==0 && dy==0) {
                    overlap++;
                    continue;
                }
				dx/=gcd_xy, dy/=gcd_xy;
				string k=to_string(dx)+"#"+to_string(dy);
				if (cnt.find(k) == cnt.end()) {
                    cnt[k] = {1, i, j};
				} else {
					cnt[k][0]++; 
				}
				if (cnt[k][0] > mx) {
					mx=cnt[k][0];
					ans[0]=i, ans[1]=j;
				} else if (cnt[k][0]==mx && i==ans[0] && j<ans[1]) {
					ans[1]=j;
				}
			}
		}
		return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值