给定两条线段(表示为起点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
输入的坐标均是有效的二维坐标
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/intersection-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
参考的思路来自于 刘汝佳 算法艺术与信息学竞赛 P357前后章节
class Solution {
public:
double precision_n = 1e-6;//符合题目条件的浮点精度
struct Point{
double x,y;
Point(){}//默认构造函数
Point(double xx,double yy):x(xx),y(yy){}//自定义构造函数
bool operator<(const Point p)const{//当交点重叠时,用于排序
if(x==p.x){
return y<p.y;
}
return x<p.x;
}
};
//行列式,叉积
double det(double x1, double y1, double x2, double y2){
return x1*y2-x2*y1;
}
//三点叉积,用来计算有向面积
double crossProduct(Point a, Point b, Point c){
return det(b.x-a.x, b.y-a.y, c.x-a.x, c.y-a.y);
}
//浮点误差处理
int dblcmp(double d){
if(fabs(d)<precision_n){return 0;}
return d>0?1:-1;
}
//点乘行列式
double dotdet(double x1, double y1, double x2, double y2){
return x1*x2+y1*y2;
}
//三点点乘
double dotProduct(Point a, Point b, Point c){
return dotdet(b.x-a.x, b.y-a.y, c.x-a.x, c.y-a.y);
}
//用来计算共线的情况下,a是否在b,c之间
int betweenCmp(Point a, Point b, Point c){
return dblcmp(dotProduct(a,b,c));
}
vector<double> intersection(vector<int>& start1, vector<int>& end1, vector<int>& start2, vector<int>& end2) {
vector<double> result;
Point result_p;
Point a(start1[0],start1[1]),b(end1[0],end1[1]),c(start2[0],start2[1]),d(end2[0],end2[1]);
//求成对叉积
double s1,s2,s3,s4;
int d1,d2,d3,d4;
d1 = dblcmp(s1 = crossProduct(a,b,c));//a,b,c有向面积
d2 = dblcmp(s2 = crossProduct(a,b,d));
d3 = dblcmp(s3 = crossProduct(c,d,a));
d4 = dblcmp(s4 = crossProduct(c,d,b));
//规范相交,就是一个标准的叉叉
if((d1^d2)==-2&&(d3^d4)==-2){//异或快速算法,1^-1=-2
result_p.x = ((c.x)*s2-(d.x)*s1)/(s2-s1);//点分比例计算面积的方法
result_p.y = ((c.y)*s2-(d.y)*s1)/(s2-s1);
result.push_back(result_p.x);result.push_back(result_p.y);
}
//非规范相交,相交点可能是四点中的一个
if(d1==0&&betweenCmp(c,a,b)<=0&&d2!=0)//c,a,b共线,且c在a,b之间,d,a,b不共线
{
result.push_back(c.x);result.push_back(c.y);
}
if(d2==0&&betweenCmp(d,a,b)<=0&&d1!=0)//d,a,b共线,且d在a,b之间,c,a,b不共线
{
result.push_back(d.x);result.push_back(d.y);
}
if(d3==0&&betweenCmp(a,c,d)<=0&&d4!=0)//a,c,d共线,且a在c,d之间
{
result.push_back(a.x);result.push_back(a.y);
}
if(d4==0&&betweenCmp(b,c,d)<=0&&d3!=0)//b,c,d共线,且b在c,d之间
{
result.push_back(b.x);result.push_back(b.y);
}
//重合
if(d1==0&&d2==0){//四点共线
//去掉分割开来的,两条共线的线的情况(虽然共线,但是完全不相交)
if(!(betweenCmp(c,a,b)>0&&betweenCmp(d,a,b)>0&&betweenCmp(a,c,d)>0)){
vector<Point> ps = {a,b,c,d};
sort(ps.begin(),ps.end());
result.push_back(ps[1].x);//选择位于中间的第二个点
result.push_back(ps[1].y);
}
}
return result;
}
};