面试题 16.03. 交点
难度困难17收藏分享切换为英文关注反馈
给定两条线段(表示为起点start = {X1, Y1}
和终点end = {X2, Y2}
),如果它们有交点,请计算其交点,没有交点则返回空值。
要求浮点型误差不超过10^-6
。若有多个交点(线段重叠)则返回 X 值最小的点,X 坐标相同则返回 Y 值最小的点。
示例 1:
输入:line1 = {0, 0}, {1, 0}line2 = {1, 1}, {0, -1} 输出: {0.5, 0}
示例 2:
输入:line1 = {0, 0}, {3, 3}line2 = {1, 1}, {2, 2} 输出: {1, 1}
示例 3:
输入:line1 = {0, 0}, {1, 1}line2 = {1, 0}, {2, 1} 输出: {},两条线段没有交点
提示:
- 坐标绝对值不会超过 2^7
- 输入的坐标均是有效的二维坐标
使用直线的一般式求解,线段的函数:Ax+By+C=0
需要实现的函数:
(1)求出A、B、C:
y=(y2-y1)/(x2-x1)*(x-x2)+y2
所以 A=y2-y1, B=x1-x2, C=x2y1-x1y2
(2)判断两个线段是否平行:
A1/B1=A2/B2, 避免除以0, A1*B2=A2*B1
(3)求并行线段的x坐标最小的交点:
如果交点存在,那么一定是两个线段的一个端点,判断端点是否在另一个线段上
(4)判断点是否在线段上:
如果两个端点是s和e,如果p在线段上,那么dis(s,p)+dis(p,e)=dis(s,e)
(5)不平行线段的交点
A1x+B1y+C1=0, A2x+B2y+C2=0
A1*A2*x+B1*A2*y+C1*A2=0
A2*A1*x+B2*A1*y+C2*A1=0
y = (C1 * A2 - C2 * A1) / (A1 * B2 - A2 * B1)
同理,x = (C2 * B1 - C1 * B2) / (A1 * B2 - A2 * B1)
如果(x,y)在两线段上,则(x,y)即为答案,否则交点不存在
#include"pch.h"
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
/*执行用时 :4 ms, 在所有 C++ 提交中击败了71.90%的用户
内存消耗 :11.5 MB, 在所有 C++ 提交中击败了100.00%的用户*/
vector<double> getABC(const vector<int> &point1, const vector<int> &point2) {
double A = point2[1] - point1[1];
double B = point1[0] - point2[0];
double C = point2[0] * point1[1] - point1[0] * point2[1];
return vector<double>{ A, B, C };
}//求出A、B、C
bool ifparallel(double A1, double B1, double A2, double B2) {
if (A1*B2 == A2 * B1) return true;
return false;
}//判断两个线段是否平行
template<typename T1, typename T2, typename T3>
bool ifinline(const T1 & s, const T2 & e, const T3 &p) {
double d1 = sqrt((p[0] - s[0])*(p[0] - s[0]) + (p[1] - s[1])*(p[1] - s[1]));
double d2 = sqrt((p[0] - e[0])*(p[0] - e[0]) + (p[1] - e[1])*(p[1] - e[1]));
double d3 = sqrt((e[0] - s[0])*(e[0] - s[0]) + (e[1] - s[1])*(e[1] - s[1]));
//要求浮点型误差不超过10^-6
if (fabs(d1 + d2 - d3) <= 1e-7) return true;
return false;
}//判断点是否在线段上
vector<double> getparllelnode(vector<int>& start1, vector<int>& end1, vector<int>& start2, vector<int>& end2) {
vector<vector<double> > res;
if (ifinline(start1, end1, start2)) res.push_back(vector<double>{double(start2[0]), double(start2[1])});
if (ifinline(start1, end1, end2)) res.push_back(vector<double>{double(end2[0]), double(end2[1])});
if (ifinline(start2, end2, start1)) res.push_back(vector<double>{double(start1[0]), double(start1[1])});
if (ifinline(start2, end2, end1)) res.push_back(vector<double>{double(end1[0]), double(end1[1])});
if (res.size() == 0) return vector<double>{};
sort(res.begin(), res.end(), [](const vector<double> &a, const vector<double> &b)->bool {
return a[0] < b[0];//求x最小的那个坐标
});
return res[0];//返回这个坐标
}//求并行线段的x坐标最小的交点
vector<double> intersection(vector<int>& start1, vector<int>& end1, vector<int>& start2, vector<int>& end2) {
vector<double> p1 = getABC(start1, end1);
vector<double> p2 = getABC(start2, end2);
if (ifparallel(p1[0], p1[1], p2[0], p2[1])) {
return getparllelnode(start1, end1, start2, end2);
}//如果平行
//不平行线段的交点
//y = (C1 * A2 - C2 * A1) / (A1 * B2 - A2 * B1)
//x = (C2 * B1 - C1 * B2) / (A1 * B2 - A2 * B1)
double y = (p1[2] * p2[0] - p2[2] * p1[0]) / (p1[0] * p2[1] - p2[0] * p1[1]);
double x = (p2[2] * p1[1] - p1[2] * p2[1]) / (p1[0] * p2[1] - p2[0] * p1[1]);
vector<double> tmp = { x,y };
if (ifinline(start1, end1, tmp) && ifinline(start2, end2, tmp)) {
return tmp;
}
return vector<double>{};
}
int main() {
vector<int> s1 = { 0,0 }; vector<int> e1 = { 1,0 };
vector<int> s2 = { 1,1 }; vector<int> e2 = { 0,-1 };
vector<double> res=intersection(s1,e1,s2,e2);
cout << res[0] << " " << res[1] << endl;
return 0;
}