LeetCode每日一题打卡✔️使用一般方法拿到双100%💯
今天出了这道题,我来试着写题解✋
题目描述
面试题 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 输入的坐标均是有效的二维坐标
隐藏提示:
所有的线都会相交吗?什么决定两条线是否相交?
无限长的线几乎总会相交,除非它们相互平行。平行线也仍然有可能“相交”——如果它们是同一条线。这对线段来说意味着什么?
我们怎样才能找到两条线的交点。如果两条线相交,那么交点必须与它们的“无限”延伸处于同一点。这两条线之间是交点吗?
仔细考虑如何处理线段具有相同斜率和与y轴相交的情况。来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/intersection-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路:两点式判断平行+参数方程求交点
震惊!LeetCode困难难度,竟然是一道普通的中学数学题
先复习一下中学学过的两点式:
上面的两点式改为乘的形式:
(y4 - y3) * (x2 - x1) == (y2 - y1) * (x4 - x3)
考虑了平行于y轴、x轴的情况也能使用。
复习一下参数方程,为了求交点:
即代码中会使用到:
double t1 = (double)(x3 * (y4 - y3) + y1 * (x4 - x3) - y3 * (x4 - x3) - x1 * (y4 - y3)) / ((x2 - x1) * (y4 - y3) - (x4 - x3) * (y2 - y1));
t1、t2的含义:
一定有且仅有一组 (t1,t2) 使得上面的二元一次方程组成立,t1 描述了交点在线段 (x1,y1)∼(x2,y2),t2同理。
以上为此题目需要的中学知识。下面我用伪代码把主要步骤梳理清楚。分支挺容易漏掉的,建议增加测试用例多测一下再提交。
伪代码
class Solution {
public double[] intersection(int[] start1, int[] end1, int[] start2, int[] end2) {
if (使用两点式,判断两线段平行,平行则为true) {
if (不在直线上) {
返回空
} else if (重合) {//在直线上
if(端点满足条件)*4 {
返回满足条件的端点中最优的//详见完整代码(解释:重合的话有四种情况(相离、一个交点、多个交点、重合),但实际题目只要求坐标值最小的点,那就肯定在题目输入的四个点之一。)
}
//不重合
返回空
}
}
} else {//两线段所在的直线相交
联立两个参数方程求解t1,t2
if(t1,t2均不在[0,1]区间,即交点不在线段上) {
返回空
} else {//交点在线段上
返回 ans = {x1 + t1 * (x2 - x1), y1 + t1 * (y2 - y1)};
}
}
}
}
伪代码里没详细写的一个细节是平行于x轴和y轴的情况下,若与 x 轴平行,只需要判断 x 的部分,y轴同理。
完整代码
class Solution {
// 注意:调用的前提是(xk,yk)已经在「直线」上
// 功能:判断 (xk, yk) 是否在「线段」(x1, y1)~(x2, y2) 上
private boolean inside(int x1, int y1, int x2, int y2, int xk, int yk) {
return (x1 == x2 || (Math.min(x1, x2) <= xk && xk <= Math.max(x1, x2))) && (y1 == y2 || (Math.min(y1, y2) <= yk && yk <= Math.max(y1, y2)));
}
//更新结果为四个点中最优值
private void update(double[] ans, double xk, double yk) {
if (ans[0] == Double.MAX_VALUE && ans[1] == Double.MAX_VALUE || xk < ans[0] || (xk == ans[0] && yk < ans[1])){
ans[0] = xk;
ans[1] = yk;
}
}
public double[] intersection(int[] start1, int[] end1, int[] start2, int[] end2) {
int x1 = start1[0];
int y1 = start1[1];
int x2 = end1[0];
int y2 = end1[1];
int x3 = start2[0];
int y3 = start2[1];
int x4 = end2[0];
int y4 = end2[1];
double[] ans = new double[2];
Arrays.fill(ans, Double.MAX_VALUE);//初始化为极大值,方便后面判断更新
//使用两点式,判断两线段平行,平行则为true
if ((y4 - y3) * (x2 - x1) == (y2 - y1) * (x4 - x3)) {
//若平行,则判断 (x3, y3) 是否在「直线」(x1, y1)~(x2, y2) 上
if ((y2 - y1) * (x3 - x1) == (y3 - y1) * (x2 - x1)) {
boolean isInside = false;
//如果重合,返回满足条件的端点中最优的
//判断 (x3, y3) 是否在「线段」(x1, y1)~(x2, y2) 上
if (inside(x1, y1, x2, y2, x3, y3)) {
isInside = true;
update(ans, (double)x3, (double)y3);
}
if (inside(x1, y1, x2, y2, x4, y4)) {
isInside = true;
update(ans, (double)x4, (double)y4);
}
if (inside(x3, y3, x4, y4, x1, y1)) {
isInside = true;
update(ans, (double)x1, (double)y1);
}
if (inside(x3, y3, x4, y4, x2, y2)) {
isInside = true;
update(ans, (double)x2, (double)y2);
}
//不重合
if (!isInside) {
return new double[0];
}
} else {//不在直线上
return new double[0];
}
} else {//两线段所在的直线相交
//联立两个参数方程求解t1,t2
double t1 = (double)(x3 * (y4 - y3) + y1 * (x4 - x3) - y3 * (x4 - x3) - x1 * (y4 - y3)) / ((x2 - x1) * (y4 - y3) - (x4 - x3) * (y2 - y1));
double t2 = (double)(x1 * (y2 - y1) + y3 * (x2 - x1) - y1 * (x2 - x1) - x3 * (y2 - y1)) / ((x4 - x3) * (y2 - y1) - (x2 - x1) * (y4 - y3));
//t1,t2均在[0,1]区间,即交点在线段上
if (t1 >= 0.0 && t1 <= 1.0 && t2 >= 0.0 && t2 <= 1.0) {
ans[0] = x1 + t1 * (x2 - x1);
ans[1] = y1 + t1 * (y2 - y1);
} else {
return new double[0];
}
}
if (ans[0] == Double.MAX_VALUE && ans[1] == Double.MAX_VALUE) {
return new double[0];
}
return ans;
}
}
提交显示双100%💯。
复杂度分析:
时间复杂度:O(1)。
空间复杂度:O(1)。
其他解法
方法二:叉积法。使用向量,进行叉积运算取模+共线处理+计算两个三角形面积后使用定比分点公式。
时间空间复杂度也是O(1)。有兴趣自行前往官方题解,链接已附在下方参考资料章节。
参考资料:
[1]题目 LeetCode 面试题 16.03. 交点https://leetcode-cn.com/problems/intersection-lcci/
[2]交点 官方题解https://leetcode-cn.com/problems/intersection-lcci/solution/jiao-dian-by-leetcode-solution/
[3]C++ 一般式求交点 作者:前额叶没长好https://leetcode-cn.com/problems/intersection-lcci/solution/c-yi-ban-shi-qiu-jiao-dian-by-time-limit/
[4]在非欧几何中,平行线能相交,怎么解释? - psai的回答 - 知乎https://www.zhihu.com/question/28620054/answer/837114817
小声哔哔环节
为什么用伪代码呢?我的理解是,代码不多,干脆写成伪代码比较直观。因为用传统排版弄成文字段落的话,读者看层次还挺费劲的,笔者也没必要就纠结markdown的多级标题,标题层次改起来没有代码方便。
如果是项目文档则另说,这里只是写OJ题解。
另外,大家听说过非欧几何吗😏,前提是在空间曲率不为0的情况下,平行线最终会交于一点。
所以,即使我们化作宇宙中的尘埃,也许终有一天也会相遇呢。哎呀骚断腿 (╯‵□′)╯︵┻━┻
好了,觉得文章有用欢迎点赞❤️收藏⭐️关注✔️转发🔗,如有疑问请留言或从博客主页找到我联系方式,下篇文章见~