最近做了一道让人抓狂的题目:题目链接:交点
简单地说,题目要求计算两条线段的交点。之所以让人抓狂,是因为相交的情况有很多种,不相交的情况也有很多种,稍微不注意就会遗漏情况,而且本身写起来就比较复杂(四个点八个数值来回计算)。
在开始解决这道题目前先给出一个小结论:
对于X轴上的线段
A
B
AB
AB和线段
C
D
CD
CD(点A坐标
(
a
,
0
)
(a,0)
(a,0),点B坐标
(
b
,
0
)
(b,0)
(b,0),点C坐标
(
c
,
0
)
(c,0)
(c,0),点D坐标
(
d
,
0
)
(d,0)
(d,0),
a
<
b
且
c
<
d
a<b且c<d
a<b且c<d),二者有交点的充要条件是:
m
a
x
(
a
,
c
)
≤
m
i
n
(
b
,
d
)
max(a, c) \le min(b,d)
max(a,c)≤min(b,d)
这个结论在这里不给出证明了,大家在纸上画画应该很容易理解。接下来,开始解决线段交点问题!
我们以线段对应的直线是否存在斜率将情况分为3种:1.两条直线都存在斜率、2.只有一条直线存在斜率和3.两条直线都不存在斜率。下面对三种情况分别讨论。
1. 两条直线都存在斜率
这种情况下首先计算出两条直线的斜率和截距k1、b1、k2、b2。
平行( k 1 = = k 2 k1==k2 k1==k2)
若 b 1 ! = b 2 b1 != b2 b1!=b2,则二者必然没有交点。
若
b
1
=
=
b
2
b1 == b2
b1==b2,说明两条直线共线,我们需要检查两条线段是否重合。如何检查呢?只需要将两条线段向X轴投影,然后用开篇的小结论判断是否有交点即可。
此时线段有多个交点,题目要求返回 X 值最小的点,X 坐标相同则返回 Y 值最小的点。可以证明,该点必然是排序(按横坐标升序,若横坐标相同按纵坐标升序)后的第二个点。
不平行( k 1 ! = k 2 k1 != k2 k1!=k2)
这种情况下直接解出交点坐标,然后判断是否在两条线段的范围内即可。
2. 只有一条直线存在斜率
这种情况下直接解出交点坐标,然后判断是否在两条线段的范围内即可。
3. 两条直线都不存在斜率
这种情况实际和情况1下的平行是类似的,我们只需要将两条线段顶点的横纵坐标交换,然后代入情况1下的平行即可。需要注意的是返回结果时需要再进行一次横纵坐标交换。
最后祭出AC code:
class Solution {
public:
vector<double> intersection(vector<int>& start1, vector<int>& end1, vector<int>& start2, vector<int>& end2) {
vector<double> ans;
bool isswap = false;
// 情况3,直接交换横纵坐标并标记
if (start1[0] == end1[0] && start2[0] == end2[0]){
isswap = true;
swap(start1[0], start1[1]); swap(end1[0], end1[1]);
swap(start2[0], start2[1]); swap(end2[0], end2[1]);
}
// 如果是情况2,通过交换使start1、end1是无斜率的,start2、end2是有斜率的,可以简化代码
else if (start2[0] == end2[0]){
swap(start1, start2); swap(end1, end2);
}
// 情况2
if (start1[0] == end1[0]){
double k2 = (double)(end2[1]-start2[1]) / (end2[0]-start2[0]), b2 = start2[1]-k2*start2[0];
double tx = start1[0], ty = k2 * tx + b2;
// 判断交点是否在范围内
if (tx >= min(start2[0], end2[0]) && tx <= max(start2[0], end2[0])){
return {tx, ty};
}
}
else{ // 情况1
double k1 = (double)(end1[1]-start1[1]) / (end1[0]-start1[0]), b1 = start1[1]-k1*start1[0];
double k2 = (double)(end2[1]-start2[1]) / (end2[0]-start2[0]), b2 = start2[1]-k2*start2[0];
if (k1 == k2){ //平行
if (b1 != b2) return {};
// 判断线段在X轴上的投影是否相交
int a = min(start1[0], end1[0]), b = max(start1[0], end1[0]);
int c = min(start2[0], end2[0]), d = max(start2[0], end2[0]);
if (max(a, c) > min(b, d)) return {};
vector<vector<int>> arr;
arr.push_back(start1); arr.push_back(start2); arr.push_back(end1); arr.push_back(end2);
sort(arr.begin(), arr.end());
vector<double> ret;
ret.push_back(arr[1][0]); ret.push_back(arr[1][1]);
if (isswap) swap(ret[0], ret[1]);
return ret;
}
// 不平行
double tx = (b2-b1) / (k1-k2), ty = k1*tx+b1;
// 检查检点是否在范围内
if (tx >= min(start1[0], end1[0]) && tx <= max(start1[0], end1[0]) && tx >= min(start2[0], end2[0]) && tx <= max(start2[0], end2[0])){
return {tx, ty};
}
}
return {};
}
};