本文涉及知识点

数学
计算几何

LeetCode面试题 16.03. 交点

给定两条线段(表示为起点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 2^727
输入的坐标均是有效的二维坐标

计算几何

用叉乘判断线段所在直线是否平行。
一,直线的表达方式,斜截式。y=kx+b。无法表达垂直于x轴的直线。
二,截距式。x a + y b = 1 \frac{x}{a}+\frac{y}{b}=1ax+by=1,无法表达平行于坐标轴的直线。
三,一般形式a x + b y + c = 0 ax+by+c=0ax+by+c=0,表示所有直线,缺点不直观。
四,参数方程。x=x0+t△ \trianglex,y=y0+t△ \triangley。
假设是线段(x1,y1)和(x2,y2)则:
{ x = x 1 + t ( x 2 − x 1 ) y = y 1 + t ( y 2 − y 1 ) \begin{cases} x=x1+t(x2-x1) \\ y=y1+t(y2-y1) \\ \end{cases}{x=x1+t(x2x1)y=y1+t(y2y1)
∀ ( x , y ) 求出的 t \forall (x,y)求出的t(x,y)求出的t如果相同,则在直线上。如果0 ≤ t ≤ 1 0 \le t \le 10t1在线段上。

平行

如果两条线段对应直线不共线,则返回空。
4个端点。就是如果共线,符合条件一定
否则返回空。
判断(x,y)是否在线段(x1,y1)(x2,y2)上。

先判断是在直线上

情况一: x 1 ≠ x 2 , y 1 ≠ y 2 x1 \neq x2,y1 \neq y2x1=x2,y1=y2
t 1 = ( x − x 1 ) ÷ ( x 2 − x 1 ) t1= (x-x1)\div (x2-x1)t1=(xx1)÷(x2x1)
t 2 = ( y − y 1 ) ÷ ( y 2 − y 1 ) t2=(y-y1)\div (y2-y1)t2=(yy1)÷(y2y1)
假如t1和t2相等,则:( x − x 1 ) ( y 2 − y 1 ) = = ( y − y 1 ) ( x 2 − x 1 ) (x-x1)(y2-y1)==(y-y1)(x2-x1)(xx1)(y2y1)==(yy1)(x2x1)式子一
情况二 x 1 = = x 2 , y 1 ≠ y 2 x1==x2,y1 \neq y2x1==x2,y1=y2
x必须等于x1,和式子一等效。
情况三: x 1 ≠ x 2 , y 1 = = y 2 x1 \neq x2,y1==y2x1=x2,y1==y2
y必须等于y1。
结论:式子一可能判断,点是否在直线上。
*扩展:令(x,y)是P,(x1,y1)和(x2,y2)是P1,P2,式子一就是P P 1 ⃗ × P 2 P 1 ⃗ = = 0 \vec{PP1}\times \vec{P2P1} == 0PP1×P2P1==0PP1和PP2平行,说明P在直线P1P2上。

判断点是否在线段上

( x − x 1 ) ( x − x 2 ) ≤ 0 且 ( y − y 1 ) ( y − y 2 ) ≤ 0 (x-x1)(x-x2)\le 0且(y-y1)(y-y2) \le 0(xx1)(xx2)0(yy1)(yy2)0

不平行。

令第一条线段的两个端点为(x1,y1)和(x2,y2),第二条线段的两个端点为(x3,y3)和(x4,y4)。
令交点为(x,y)则:
x = x 1 + t 1 ( x 2 − x 1 ) = x 3 + t 2 ( x 4 − x 3 ) x=x1+t1(x2-x1)=x3+t2(x4-x3)x=x1+t1(x2x1)=x3+t2(x4x3)式子一
y = y 1 + t 1 ( y 2 − y 1 ) = y 3 + t 2 ( y 4 − y 3 ) y=y1+t1(y2-y1)=y3+t2(y4-y3)y=y1+t1(y2y1)=y3+t2(y4y3)式子二
情况一:x 1 ≠ x 2 且 y 1 ≠ y 2 x1 \neq x2 且 y1 \neq y2x1=x2y1=y2
式子一乘以(y2-y1)
( x 1 − x 3 ) ( y 2 − y 1 ) + t 1 ( x 2 − x 1 ) ( y 2 − y 1 ) = t 2 ( x 4 − x 3 ) ( y 2 − y 1 ) (x1-x3)(y2-y1)+t1(x2-x1)(y2-y1)=t2(x4-x3)(y2-y1)(x1x3)(y2y1)+t1(x2x1)(y2y1)=t2(x4x3)(y2y1)
式子二乘以(x2-x1)
( y 1 − y 3 ) ( x 2 − x 1 ) + t 1 ( x 2 − x 1 ) ( y 2 − y 1 ) = t 2 ( y 4 − y 3 ) ( x 2 − x 1 ) (y1-y3)(x2-x1)+t1(x2-x1)(y2-y1)=t2(y4-y3)(x2-x1)(y1y3)(x2x1)+t1(x2x1)(y2y1)=t2(y4y3)(x2x1)
两者相减:
( x 1 − x 3 ) ( y 2 − y 1 ) − ( y 1 − y 3 ) ( x 2 − x 1 ) = t 2 [ ( x 4 − x 3 ) ( y 2 − y 1 ) − ( y 4 − y 3 ) ( x 2 − x 1 ) ] (x1-x3)(y2-y1)-(y1-y3)(x2-x1)=t2[(x4-x3)(y2-y1)-(y4-y3)(x2-x1)](x1x3)(y2y1)(y1y3)(x2x1)=t2[(x4x3)(y2y1)(y4y3)(x2x1)]
→ t 2 = [ ( x 1 − x 3 ) ( y 2 − y 1 ) − ( y 1 − y 3 ) ( x 2 − x 1 ) ] ÷ [ ( x 4 − x 3 ) ( y 2 − y 1 ) − ( y 4 − y 3 ) ( x 2 − x 1 ) ] \rightarrow t2 =[(x1-x3)(y2-y1)-(y1-y3)(x2-x1)] \div [(x4-x3)(y2-y1)-(y4-y3)(x2-x1)]t2=[(x1x3)(y2y1)(y1y3)(x2x1)]÷[(x4x3)(y2y1)(y4y3)(x2x1)]式子三
如果分母为0,根据叉乘为0,则两矢量方向相同或想法,即两直线平行。
同理:
t 1 = [ ( x 3 − x 1 ) ( y 4 − y 3 ) − ( y 3 − y 1 ) ( x 4 − x 3 ) ] ÷ [ ( x 2 − x 1 ) ( y 4 − y 3 ) − ( x 4 − x 3 ) ( y 2 − y 1 ) ] t1=[(x3-x1)(y4-y3)-(y3-y1)(x4-x3)]\div[(x2-x1)(y4-y3)-(x4-x3)(y2-y1)]t1=[(x3x1)(y4y3)(y3y1)(x4x3)]÷[(x2x1)(y4y3)(x4x3)(y2y1)]式子四
情况二:x1 == x2,y 1 ≠ y 2 y1 \neq y2y1=y2
即线段一,垂直于x轴。线段二不垂直于x轴,即x 4 ≠ x 3 x4 \neq x3x4=x3
x1=x3+t2(x4-x3)→ t 2 = ( x 1 − x 3 ) ÷ ( x 4 − x 3 ) \rightarrow t2=(x1-x3)\div (x4-x3)t2=(x1x3)÷(x4x3)
对式子三,用x1替换x2。
t 2 = ( x 1 − x 3 ) ( y 2 − y 1 ) ÷ ( x 4 − x 3 ) ( y 2 − y 1 ) = ( x 1 − x 3 ) ÷ ( x 4 − x 3 ) t2 =(x1-x3)(y2-y1)\div (x4-x3)(y2-y1)=(x1-x3)\div(x4-x3)t2=(x1x3)(y2y1)÷(x4x3)(y2y1)=(x1x3)÷(x4x3)即t2无需特殊处理。
情况三:y1==y2,x 1 ≠ x 2 x1 \neq x2x1=x2,即线段一垂直于y轴。线段二不平行于y轴。y 3 ≠ y 4 y3 \neq y4y3=y4
对式子二,y1代替y2。y 1 = y 3 + t 2 ( y 4 − y 3 ) → t 2 = ( y 1 − y 3 ) ÷ ( y 4 − y 3 ) y1=y3+t2(y4-y3)\rightarrow t2=(y1-y3)\div (y4-y3)y1=y3+t2(y4y3)t2=(y1y3)÷(y4y3)
对于式子三,用y1代替y2。− ( y 1 − y 3 ) ( x 2 − x 1 ) ÷ − ( y 4 − y 3 ) ( x 2 − x 1 ) = ( y 1 − y 3 ) ÷ ( y 4 − y 3 ) -(y1-y3)(x2-x1)\div-(y4-y3)(x2-x1)=(y1-y3)\div(y4-y3)(y1y3)(x2x1)÷(y4y3)(x2x1)=(y1y3)÷(y4y3)
即t2无需特殊处理。
情况四:y 1 = = y 2 , x 1 = = x 2 y1 == y2 ,x1 == x2y1==y2,x1==x2,点重合,与提议矛盾。
总结:计算t2无需特殊处理,t2就可以计算出交点。

代码

核心代码

int CrossMul(int x1, int y1, int x2, int y2) {
return x1*y2 - x2*y1;
}
bool PointInLine(int x, int y, int x1, int y1, int x2, int y2) {
return 0 == CrossMul(x - x1, y - y1, x - x2, y - y2);
}
bool PointInSegMent(int x, int y, int x1, int y1, int x2, int y2) {
if (!PointInLine(x, y, x1, y1, x2, y2)) { return false; }
return ((x - x1) * (x - x2) <= 0) && ((y - y1)*(y - y2) <= 0);
}
class Solution {
public:
vector<double> intersection(vector<int>& start1, vector<int>& end1, vector<int>& start2, vector<int>& end2) {
  const int x1 = start1[0], x2 = end1[0], x3 = start2[0], x4 = end2[0];
  const int y1 = start1[1], y2 = end1[1], y3 = start2[1], y4 = end2[1];
  const int iCM = CrossMul(x4 - x3, y4 - y3, x2 - x1, y2 - y1);
  if (0 != iCM) {//不处理平行
  double t2 = CrossMul(x1 - x3, y1 - y3, x2 - x1, y2 - y1)/(double)iCM;
  const double x = x3 + t2 * (x4 - x3);
  const double y = y3 + t2 * (y4 - y3);
  if (((x - x1) * (x - x2) <= 0) && ((x - x3) * (x - x4) <= 0) && ((y - y1) * (y - y2) <= 0) && ((y - y3) * (y - y4) <= 0)) {
  return {x,y};
  }
  return {};
  }
  vector<vector<double>> ans;
    if (PointInSegMent(x1, y1, x3, y3, x4, y4)) {
    ans.emplace_back(vector<double>{ (double)x1,(double)y1 });
      }
      if (PointInSegMent(x2, y2, x3, y3, x4, y4)) {
      ans.emplace_back(vector<double>{ (double)x2, (double)y2 });
        }
        if (PointInSegMent(x3, y3, x1, y1, x2, y2)) {
        ans.emplace_back(vector<double>{ (double)x3, (double)y3 });
          }
          if (PointInSegMent(x4, y4, x1, y1, x2, y2)) {
          ans.emplace_back(vector<double>{ (double)x4, (double)y4 });
            }
            sort(ans.begin(), ans.end());
            return ans.size() ? ans[0] : vector<double>{};
              }
              };

扩展阅读

我想对大家说的话
工作中遇到的挑战,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
高效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
员工说:手艺至上,老板不信;投资人的代表说:技术至上,老板会信。
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019C++17
或者 操作系统:win10 开发环境: VS2022C++17
如无特殊说明,本算法用**C++**实现。